MetaList 0.6.2__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.2 → metalist-0.6.3}/PKG-INFO +14 -3
  2. {metalist-0.6.2 → metalist-0.6.3}/README.md +13 -2
  3. {metalist-0.6.2 → 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.2 → metalist-0.6.3}/app/exception_boundaries.py +7 -0
  6. {metalist-0.6.2 → metalist-0.6.3}/app/https_proxy.py +9 -3
  7. {metalist-0.6.2 → 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.2 → metalist-0.6.3}/app/services/client_state_service.py +5 -1
  11. {metalist-0.6.2 → metalist-0.6.3}/app/services/self_update.py +6 -0
  12. {metalist-0.6.2 → metalist-0.6.3}/app/services/shell_session_service.py +1 -1
  13. {metalist-0.6.2 → metalist-0.6.3}/app/services/update_preflight.py +5 -0
  14. {metalist-0.6.2 → metalist-0.6.3}/app/services/windows_process_control.py +37 -4
  15. {metalist-0.6.2 → metalist-0.6.3}/app/startup_sanity_config.py +3 -0
  16. {metalist-0.6.2 → metalist-0.6.3}/app/static/config/command_palette_tags.json +1 -1
  17. {metalist-0.6.2 → metalist-0.6.3}/app/static/css/main.css +16 -0
  18. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/main.js +3 -0
  19. metalist-0.6.3/app/static/js/modules/app-update-service.js +106 -0
  20. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/auth.js +1 -0
  21. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/command-palette/command-palette-controller.js +5 -0
  22. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/config.js +1 -0
  23. metalist-0.6.3/app/static/js/modules/modals/version-info-modal.js +313 -0
  24. {metalist-0.6.2 → metalist-0.6.3}/app/version.py +1 -1
  25. {metalist-0.6.2 → metalist-0.6.3}/metalist.egg-info/PKG-INFO +14 -3
  26. {metalist-0.6.2 → metalist-0.6.3}/metalist.egg-info/SOURCES.txt +4 -0
  27. metalist-0.6.2/app/static/js/modules/modals/version-info-modal.js +0 -190
  28. {metalist-0.6.2 → metalist-0.6.3}/MANIFEST.in +0 -0
  29. {metalist-0.6.2 → metalist-0.6.3}/app/__init__.py +0 -0
  30. {metalist-0.6.2 → metalist-0.6.3}/app/api/__init__.py +0 -0
  31. {metalist-0.6.2 → metalist-0.6.3}/app/api/deps.py +0 -0
  32. {metalist-0.6.2 → metalist-0.6.3}/app/api/dev.py +0 -0
  33. {metalist-0.6.2 → metalist-0.6.3}/app/api/note_requests.py +0 -0
  34. {metalist-0.6.2 → metalist-0.6.3}/app/api/request_auth.py +0 -0
  35. {metalist-0.6.2 → metalist-0.6.3}/app/api/routes/ai.py +0 -0
  36. {metalist-0.6.2 → metalist-0.6.3}/app/api/routes/auth.py +0 -0
  37. {metalist-0.6.2 → metalist-0.6.3}/app/api/routes/backups.py +0 -0
  38. {metalist-0.6.2 → metalist-0.6.3}/app/api/routes/files.py +0 -0
  39. {metalist-0.6.2 → metalist-0.6.3}/app/api/routes/notes.py +0 -0
  40. {metalist-0.6.2 → metalist-0.6.3}/app/api/routes/ontology.py +0 -0
  41. {metalist-0.6.2 → metalist-0.6.3}/app/api/routes/reminders.py +0 -0
  42. {metalist-0.6.2 → metalist-0.6.3}/app/api/routes/remote_images.py +0 -0
  43. {metalist-0.6.2 → metalist-0.6.3}/app/api/routes/test.py +0 -0
  44. {metalist-0.6.2 → metalist-0.6.3}/app/api/transactions.py +0 -0
  45. {metalist-0.6.2 → metalist-0.6.3}/app/api/upload_limits.py +0 -0
  46. {metalist-0.6.2 → metalist-0.6.3}/app/config.py +0 -0
  47. {metalist-0.6.2 → metalist-0.6.3}/app/data_directory.py +0 -0
  48. {metalist-0.6.2 → metalist-0.6.3}/app/db/__init__.py +0 -0
  49. {metalist-0.6.2 → metalist-0.6.3}/app/db/backup_settings_sql.py +0 -0
  50. {metalist-0.6.2 → metalist-0.6.3}/app/db/engine.py +0 -0
  51. {metalist-0.6.2 → metalist-0.6.3}/app/db/file_schema.py +0 -0
  52. {metalist-0.6.2 → metalist-0.6.3}/app/db/file_session.py +0 -0
  53. {metalist-0.6.2 → metalist-0.6.3}/app/db/files_sql.py +0 -0
  54. {metalist-0.6.2 → metalist-0.6.3}/app/db/link_titles_sql.py +0 -0
  55. {metalist-0.6.2 → metalist-0.6.3}/app/db/live_recovery.py +0 -0
  56. {metalist-0.6.2 → metalist-0.6.3}/app/db/migrations.py +0 -0
  57. {metalist-0.6.2 → metalist-0.6.3}/app/db/notes_sql.py +0 -0
  58. {metalist-0.6.2 → metalist-0.6.3}/app/db/ontology_rules_sql.py +0 -0
  59. {metalist-0.6.2 → metalist-0.6.3}/app/db/reminders_sql.py +0 -0
  60. {metalist-0.6.2 → metalist-0.6.3}/app/db/retire_sounds.py +0 -0
  61. {metalist-0.6.2 → metalist-0.6.3}/app/db/schema.py +0 -0
  62. {metalist-0.6.2 → metalist-0.6.3}/app/db/search_history_sql.py +0 -0
  63. {metalist-0.6.2 → metalist-0.6.3}/app/db/session.py +0 -0
  64. {metalist-0.6.2 → metalist-0.6.3}/app/db/settings_sql.py +0 -0
  65. {metalist-0.6.2 → metalist-0.6.3}/app/db/tab_state_sql.py +0 -0
  66. {metalist-0.6.2 → metalist-0.6.3}/app/db/version.py +0 -0
  67. {metalist-0.6.2 → metalist-0.6.3}/app/encryption_audit.py +0 -0
  68. {metalist-0.6.2 → metalist-0.6.3}/app/models/commands.py +0 -0
  69. {metalist-0.6.2 → metalist-0.6.3}/app/models/database.py +0 -0
  70. {metalist-0.6.2 → metalist-0.6.3}/app/models/entities.py +0 -0
  71. {metalist-0.6.2 → metalist-0.6.3}/app/models/enums.py +0 -0
  72. {metalist-0.6.2 → metalist-0.6.3}/app/models/linked_list.py +0 -0
  73. {metalist-0.6.2 → metalist-0.6.3}/app/models/list_operations.py +0 -0
  74. {metalist-0.6.2 → metalist-0.6.3}/app/models/list_traversal.py +0 -0
  75. {metalist-0.6.2 → metalist-0.6.3}/app/models/note_crud.py +0 -0
  76. {metalist-0.6.2 → metalist-0.6.3}/app/models/utils.py +0 -0
  77. {metalist-0.6.2 → metalist-0.6.3}/app/presentation/__init__.py +0 -0
  78. {metalist-0.6.2 → metalist-0.6.3}/app/presentation/render/__init__.py +0 -0
  79. {metalist-0.6.2 → metalist-0.6.3}/app/presentation/render/note_renderer.py +0 -0
  80. {metalist-0.6.2 → metalist-0.6.3}/app/presentation/templates.py +0 -0
  81. {metalist-0.6.2 → metalist-0.6.3}/app/security/authenticated_logging.py +0 -0
  82. {metalist-0.6.2 → metalist-0.6.3}/app/security/encryption.py +0 -0
  83. {metalist-0.6.2 → metalist-0.6.3}/app/security/http_headers.py +0 -0
  84. {metalist-0.6.2 → metalist-0.6.3}/app/security/note_html.py +0 -0
  85. {metalist-0.6.2 → metalist-0.6.3}/app/security/request_boundary.py +0 -0
  86. {metalist-0.6.2 → metalist-0.6.3}/app/security/sensitive_cache.py +0 -0
  87. {metalist-0.6.2 → metalist-0.6.3}/app/security/sensitive_logging.py +0 -0
  88. {metalist-0.6.2 → metalist-0.6.3}/app/security/shell_execution.py +0 -0
  89. {metalist-0.6.2 → metalist-0.6.3}/app/security/validation_errors.py +0 -0
  90. {metalist-0.6.2 → metalist-0.6.3}/app/server_runtime.py +0 -0
  91. {metalist-0.6.2 → metalist-0.6.3}/app/services/__init__.py +0 -0
  92. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/__init__.py +0 -0
  93. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/actions.py +0 -0
  94. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/cloud_privacy.py +0 -0
  95. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/context.py +0 -0
  96. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/evidence_serialization.py +0 -0
  97. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/inference.py +0 -0
  98. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/investigation.py +0 -0
  99. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/model_policy.py +0 -0
  100. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/ollama_inference.py +0 -0
  101. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/openai_cost_tracking.py +0 -0
  102. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/openai_inference.py +0 -0
  103. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/permissions.py +0 -0
  104. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/prompt_settings.py +0 -0
  105. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/prompts/__init__.py +0 -0
  106. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/prompts/final-response.md +0 -0
  107. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/prompts/system.md +0 -0
  108. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/prompts/tool-result.md +0 -0
  109. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/retrieval_settings.py +0 -0
  110. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/runtime.py +0 -0
  111. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/scope.py +0 -0
  112. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/skill_settings.py +0 -0
  113. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/skills/__init__.py +0 -0
  114. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/skills/scoped-investigation.md +0 -0
  115. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/tagging.py +0 -0
  116. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/tagging_run.py +0 -0
  117. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/token_estimation.py +0 -0
  118. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/tools.py +0 -0
  119. {metalist-0.6.2 → metalist-0.6.3}/app/services/agent/trace.py +0 -0
  120. {metalist-0.6.2 → metalist-0.6.3}/app/services/ai_chat.py +0 -0
  121. {metalist-0.6.2 → metalist-0.6.3}/app/services/ai_chat_rendering.py +0 -0
  122. {metalist-0.6.2 → metalist-0.6.3}/app/services/ai_chat_stream.py +0 -0
  123. {metalist-0.6.2 → metalist-0.6.3}/app/services/auth.py +0 -0
  124. {metalist-0.6.2 → metalist-0.6.3}/app/services/auth_cache_state.py +0 -0
  125. {metalist-0.6.2 → metalist-0.6.3}/app/services/auth_service.py +0 -0
  126. {metalist-0.6.2 → metalist-0.6.3}/app/services/backlink_index.py +0 -0
  127. {metalist-0.6.2 → metalist-0.6.3}/app/services/backlinks.py +0 -0
  128. {metalist-0.6.2 → metalist-0.6.3}/app/services/backup_service.py +0 -0
  129. {metalist-0.6.2 → metalist-0.6.3}/app/services/backup_settings_service.py +0 -0
  130. {metalist-0.6.2 → metalist-0.6.3}/app/services/bulk_operation.py +0 -0
  131. {metalist-0.6.2 → metalist-0.6.3}/app/services/content_cache.py +0 -0
  132. {metalist-0.6.2 → metalist-0.6.3}/app/services/content_formatting.py +0 -0
  133. {metalist-0.6.2 → metalist-0.6.3}/app/services/diagnostics.py +0 -0
  134. {metalist-0.6.2 → metalist-0.6.3}/app/services/embedded_references.py +0 -0
  135. {metalist-0.6.2 → metalist-0.6.3}/app/services/encryption.py +0 -0
  136. {metalist-0.6.2 → metalist-0.6.3}/app/services/exception_capture.py +0 -0
  137. {metalist-0.6.2 → metalist-0.6.3}/app/services/file_registry.py +0 -0
  138. {metalist-0.6.2 → metalist-0.6.3}/app/services/file_storage.py +0 -0
  139. {metalist-0.6.2 → metalist-0.6.3}/app/services/footnote_layout.py +0 -0
  140. {metalist-0.6.2 → metalist-0.6.3}/app/services/footnote_rendering.py +0 -0
  141. {metalist-0.6.2 → metalist-0.6.3}/app/services/hierarchy.py +0 -0
  142. {metalist-0.6.2 → metalist-0.6.3}/app/services/html_export.py +0 -0
  143. {metalist-0.6.2 → metalist-0.6.3}/app/services/html_unformatting.py +0 -0
  144. {metalist-0.6.2 → metalist-0.6.3}/app/services/hydration_state.py +0 -0
  145. {metalist-0.6.2 → metalist-0.6.3}/app/services/image_size_formatting.py +0 -0
  146. {metalist-0.6.2 → metalist-0.6.3}/app/services/inline_image_occurrences.py +0 -0
  147. {metalist-0.6.2 → metalist-0.6.3}/app/services/input_errors.py +0 -0
  148. {metalist-0.6.2 → metalist-0.6.3}/app/services/integrity.py +0 -0
  149. {metalist-0.6.2 → metalist-0.6.3}/app/services/latex_rendering.py +0 -0
  150. {metalist-0.6.2 → metalist-0.6.3}/app/services/link_titles.py +0 -0
  151. {metalist-0.6.2 → metalist-0.6.3}/app/services/login_rate_limit.py +0 -0
  152. {metalist-0.6.2 → metalist-0.6.3}/app/services/maintenance_mode.py +0 -0
  153. {metalist-0.6.2 → metalist-0.6.3}/app/services/managed_ollama_runtime.py +0 -0
  154. {metalist-0.6.2 → metalist-0.6.3}/app/services/markdown_rendering.py +0 -0
  155. {metalist-0.6.2 → metalist-0.6.3}/app/services/namespace_deletion_jobs.py +0 -0
  156. {metalist-0.6.2 → metalist-0.6.3}/app/services/namespace_deletion_worker.py +0 -0
  157. {metalist-0.6.2 → metalist-0.6.3}/app/services/namespace_rename_jobs.py +0 -0
  158. {metalist-0.6.2 → metalist-0.6.3}/app/services/namespace_rename_worker.py +0 -0
  159. {metalist-0.6.2 → metalist-0.6.3}/app/services/namespace_runtime_guard.py +0 -0
  160. {metalist-0.6.2 → metalist-0.6.3}/app/services/namespace_switcher.py +0 -0
  161. {metalist-0.6.2 → metalist-0.6.3}/app/services/note_fullscreen.py +0 -0
  162. {metalist-0.6.2 → metalist-0.6.3}/app/services/note_image_tags.py +0 -0
  163. {metalist-0.6.2 → metalist-0.6.3}/app/services/note_ordering.py +0 -0
  164. {metalist-0.6.2 → metalist-0.6.3}/app/services/note_store.py +0 -0
  165. {metalist-0.6.2 → metalist-0.6.3}/app/services/ollama_provider.py +0 -0
  166. {metalist-0.6.2 → metalist-0.6.3}/app/services/ontology_rules_store.py +0 -0
  167. {metalist-0.6.2 → metalist-0.6.3}/app/services/openai_credentials.py +0 -0
  168. {metalist-0.6.2 → metalist-0.6.3}/app/services/password_note_fields.py +0 -0
  169. {metalist-0.6.2 → metalist-0.6.3}/app/services/reference_presentation.py +0 -0
  170. {metalist-0.6.2 → metalist-0.6.3}/app/services/reminders.py +0 -0
  171. {metalist-0.6.2 → metalist-0.6.3}/app/services/remote_image_proxy.py +0 -0
  172. {metalist-0.6.2 → metalist-0.6.3}/app/services/request_recovery.py +0 -0
  173. {metalist-0.6.2 → metalist-0.6.3}/app/services/resource_limits.py +0 -0
  174. {metalist-0.6.2 → metalist-0.6.3}/app/services/root_sorting.py +0 -0
  175. {metalist-0.6.2 → metalist-0.6.3}/app/services/runtime_generation.py +0 -0
  176. {metalist-0.6.2 → metalist-0.6.3}/app/services/runtime_hardening.py +0 -0
  177. {metalist-0.6.2 → metalist-0.6.3}/app/services/runtime_lock.py +0 -0
  178. {metalist-0.6.2 → metalist-0.6.3}/app/services/search_history.py +0 -0
  179. {metalist-0.6.2 → metalist-0.6.3}/app/services/search_history_storage.py +0 -0
  180. {metalist-0.6.2 → metalist-0.6.3}/app/services/search_index.py +0 -0
  181. {metalist-0.6.2 → metalist-0.6.3}/app/services/search_query.py +0 -0
  182. {metalist-0.6.2 → metalist-0.6.3}/app/services/search_text.py +0 -0
  183. {metalist-0.6.2 → metalist-0.6.3}/app/services/selected_text_tag.py +0 -0
  184. {metalist-0.6.2 → metalist-0.6.3}/app/services/session_timeout_service.py +0 -0
  185. {metalist-0.6.2 → metalist-0.6.3}/app/services/snapshot.py +0 -0
  186. {metalist-0.6.2 → metalist-0.6.3}/app/services/store.py +0 -0
  187. {metalist-0.6.2 → metalist-0.6.3}/app/services/structured_note_renderers.py +0 -0
  188. {metalist-0.6.2 → metalist-0.6.3}/app/services/sync.py +0 -0
  189. {metalist-0.6.2 → metalist-0.6.3}/app/services/sync_state.py +0 -0
  190. {metalist-0.6.2 → metalist-0.6.3}/app/services/tab_state.py +0 -0
  191. {metalist-0.6.2 → metalist-0.6.3}/app/services/tag_case.py +0 -0
  192. {metalist-0.6.2 → metalist-0.6.3}/app/services/tag_ontology.py +0 -0
  193. {metalist-0.6.2 → metalist-0.6.3}/app/services/tag_rename.py +0 -0
  194. {metalist-0.6.2 → metalist-0.6.3}/app/services/tag_suggestions.py +0 -0
  195. {metalist-0.6.2 → metalist-0.6.3}/app/services/tag_term_matching.py +0 -0
  196. {metalist-0.6.2 → metalist-0.6.3}/app/services/test_reset.py +0 -0
  197. {metalist-0.6.2 → metalist-0.6.3}/app/services/tokens.py +0 -0
  198. {metalist-0.6.2 → metalist-0.6.3}/app/services/undo_state.py +0 -0
  199. {metalist-0.6.2 → metalist-0.6.3}/app/services/update_backups.py +0 -0
  200. {metalist-0.6.2 → metalist-0.6.3}/app/services/view_cache.py +0 -0
  201. {metalist-0.6.2 → metalist-0.6.3}/app/services/view_diff.py +0 -0
  202. {metalist-0.6.2 → metalist-0.6.3}/app/services/view_state.py +0 -0
  203. {metalist-0.6.2 → metalist-0.6.3}/app/startup_environment.py +0 -0
  204. {metalist-0.6.2 → metalist-0.6.3}/app/startup_js_sanity.py +0 -0
  205. {metalist-0.6.2 → metalist-0.6.3}/app/startup_sanity.py +0 -0
  206. {metalist-0.6.2 → metalist-0.6.3}/app/static/css/floating-notes.css +0 -0
  207. {metalist-0.6.2 → metalist-0.6.3}/app/static/css/forms.css +0 -0
  208. {metalist-0.6.2 → metalist-0.6.3}/app/static/img/favicon.ico +0 -0
  209. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/locked.js +0 -0
  210. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/activity-tracker.js +0 -0
  211. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/ai-chat/agent-prompt-service.js +0 -0
  212. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/ai-chat/agent-retrieval-settings.js +0 -0
  213. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-agent-debug-view.js +0 -0
  214. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-chat-api.js +0 -0
  215. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-chat-panel-controller.js +0 -0
  216. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-chat-panel-service.js +0 -0
  217. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-thinking-level-service.js +0 -0
  218. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/ai-chat/bulk-elapsed-clock.js +0 -0
  219. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/ai-chat/bulk-proposal-ui.js +0 -0
  220. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/ai-chat/cloud-privacy-policy.js +0 -0
  221. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/ai-chat/proposal-menu.js +0 -0
  222. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/api-client.js +0 -0
  223. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/api-failure-classification-service.js +0 -0
  224. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/application-state.js +0 -0
  225. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/async-result.js +0 -0
  226. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/client-state-api.js +0 -0
  227. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/client-state-migration.js +0 -0
  228. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/command-palette/activation-auth-policy.js +0 -0
  229. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/command-palette/backup-command-availability.js +0 -0
  230. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/command-palette/click-activation-service.js +0 -0
  231. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/command-palette/endpoint-registry.js +0 -0
  232. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/command-palette/note-layout-preferences.js +0 -0
  233. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/command-palette/preferences-store.js +0 -0
  234. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/command-palette/tag-config-loader.js +0 -0
  235. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/command-palette/usage-store.js +0 -0
  236. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/connectivity-monitor.js +0 -0
  237. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/context-menu/context-menu-registry.js +0 -0
  238. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/context-menu/context-menu-service.js +0 -0
  239. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/context-menu/context-menu-target-service.js +0 -0
  240. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/context-menu/input-caret-service.js +0 -0
  241. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/disconnect-editing-state-service.js +0 -0
  242. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/dom-utils-fixed.js +0 -0
  243. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/dom-utils.js +0 -0
  244. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/editor-commands.js +0 -0
  245. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/editor-selection.js +0 -0
  246. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/editor-toolbar.js +0 -0
  247. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/elapsed-time.js +0 -0
  248. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/error-handler.js +0 -0
  249. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/error-overlay.js +0 -0
  250. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/expected-errors.js +0 -0
  251. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/location-flags.js +0 -0
  252. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/login-namespace-picker.js +0 -0
  253. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/maintenance.js +0 -0
  254. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/agent-prompt-editor-modal.js +0 -0
  255. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/ai-agent-settings-modal.js +0 -0
  256. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/alphabetize-root-notes-modal.js +0 -0
  257. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/backup-restore-modal.js +0 -0
  258. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/backup-result-modal.js +0 -0
  259. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/backup-retention-modal.js +0 -0
  260. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/backup-settings-modal.js +0 -0
  261. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/base-modal.js +0 -0
  262. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/confirmation-modal.js +0 -0
  263. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/delete-namespace-modal.js +0 -0
  264. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/delete-namespace-validation.js +0 -0
  265. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/help-modal.js +0 -0
  266. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/namespace-loading-page.js +0 -0
  267. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/namespace-modals.js +0 -0
  268. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/namespace-ports-profile.js +0 -0
  269. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/namespace-rename-validation.js +0 -0
  270. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/note-layout-appearance-modal.js +0 -0
  271. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/ontology-modal.js +0 -0
  272. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/password-modal.js +0 -0
  273. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/prioritize-modal.js +0 -0
  274. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/random-password-modal.js +0 -0
  275. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/reminder-modal.js +0 -0
  276. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/reset-updated-at-modal.js +0 -0
  277. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/search-suggestion-statistics-modal.js +0 -0
  278. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/modals/session-timeout-modal.js +0 -0
  279. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/content-actions.js +0 -0
  280. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/history-actions.js +0 -0
  281. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/note-actions.js +0 -0
  282. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/search-actions.js +0 -0
  283. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/selection-actions.js +0 -0
  284. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/ui-actions.js +0 -0
  285. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/context-menu-events.js +0 -0
  286. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/focus-events.js +0 -0
  287. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/inactivity-events.js +0 -0
  288. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/input-events.js +0 -0
  289. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/keyboard-events.js +0 -0
  290. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/mouse-events.js +0 -0
  291. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/search-events.js +0 -0
  292. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/mode-context.js +0 -0
  293. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/mode-logger.js +0 -0
  294. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/mode-manager-controller.js +0 -0
  295. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/add-style-service.js +0 -0
  296. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/animated-scroll-service.js +0 -0
  297. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/backlinks-navigation-service.js +0 -0
  298. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/backlinks-panel-service.js +0 -0
  299. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/clipboard-shortcut-policy-service.js +0 -0
  300. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/collapse-affordance-service.js +0 -0
  301. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/collapse-editing-policy-service.js +0 -0
  302. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/command-gate-service.js +0 -0
  303. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/context-menu-target-service.js +0 -0
  304. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/deselect-selection-state-service.js +0 -0
  305. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/differential-view-service.js +0 -0
  306. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/edit-session-collapse-policy-service.js +0 -0
  307. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/edit-session-collapse-service.js +0 -0
  308. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/embedded-image-service.js +0 -0
  309. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/file-drop-policy-service.js +0 -0
  310. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/file-image-preview-service.js +0 -0
  311. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/file-reference-service.js +0 -0
  312. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/filtered-refresh-selection-service.js +0 -0
  313. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/floating-note-dom.js +0 -0
  314. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/floating-note-layout.js +0 -0
  315. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/floating-note-service.js +0 -0
  316. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/footnote-navigation-service.js +0 -0
  317. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/history-selection-policy-service.js +0 -0
  318. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/history-shortcut-policy-service.js +0 -0
  319. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/html-paste-sanitizer-service.js +0 -0
  320. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/image-context-menu-action-service.js +0 -0
  321. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/image-file-insert-choice-modal-service.js +0 -0
  322. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/infinite-scroll-service.js +0 -0
  323. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/link-context-menu-action-service.js +0 -0
  324. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/link-title-prefetch-service.js +0 -0
  325. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/markdown-render-service.js +0 -0
  326. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/mermaid-render-service.js +0 -0
  327. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-click-target-service.js +0 -0
  328. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-clipboard-write-service.js +0 -0
  329. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-collapse-animation-service.js +0 -0
  330. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-context-menu-action-service.js +0 -0
  331. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-delete-shortcut-policy-service.js +0 -0
  332. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-drag-service.js +0 -0
  333. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-fullscreen-service.js +0 -0
  334. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-reposition-animation-service.js +0 -0
  335. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-split-service.js +0 -0
  336. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-timestamp-hover-service.js +0 -0
  337. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/password-clipboard-service.js +0 -0
  338. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/polling-service.js +0 -0
  339. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/reference-source-navigation-service.js +0 -0
  340. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/remote-image-proxy-service.js +0 -0
  341. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/remove-formatting-service.js +0 -0
  342. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/root-date-separator-service.js +0 -0
  343. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/root-sort-indicator-service.js +0 -0
  344. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/root-sort-service.js +0 -0
  345. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/scroll-anchor-service.js +0 -0
  346. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/scroll-restoration-service.js +0 -0
  347. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/scroll-to-top-service.js +0 -0
  348. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-contexts-overlay-service.js +0 -0
  349. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-debounce-service.js +0 -0
  350. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-input-service.js +0 -0
  351. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-interaction-service.js +0 -0
  352. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-redaction-reveal-service.js +0 -0
  353. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestion-statistics-service.js +0 -0
  354. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestion-window-labels-service.js +0 -0
  355. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestion-windows-service.js +0 -0
  356. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestions-service.js +0 -0
  357. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-syntax-service.js +0 -0
  358. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/selected-formatting-removal-service.js +0 -0
  359. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/selected-text-tag-service.js +0 -0
  360. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/shell-output-service.js +0 -0
  361. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-dom-cache-service.js +0 -0
  362. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-drag-service.js +0 -0
  363. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-duplication-service.js +0 -0
  364. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-state-service.js +0 -0
  365. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-bar-service.js +0 -0
  366. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-input-selection-service.js +0 -0
  367. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-proposal-service.js +0 -0
  368. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-suggestions-service.js +0 -0
  369. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-syntax-service.js +0 -0
  370. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/todo-toggle-editing-service.js +0 -0
  371. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/untagged-view-indicator-service.js +0 -0
  372. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/view-mode-search-shortcut-service.js +0 -0
  373. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/note-html-sanitizer.js +0 -0
  374. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/password-generator.js +0 -0
  375. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/password-operation-response.js +0 -0
  376. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/password-policy.js +0 -0
  377. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/password-strength.js +0 -0
  378. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/reminder-store.js +0 -0
  379. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/reminder-surface-service.js +0 -0
  380. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/selection-markers.js +0 -0
  381. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/session-auth.js +0 -0
  382. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/tag-token.js +0 -0
  383. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/modules/uuid.js +0 -0
  384. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/namespace-deleted.js +0 -0
  385. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/namespace-renamed.js +0 -0
  386. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/vendor/dompurify-3.4.13.LICENSE.txt +0 -0
  387. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/vendor/dompurify-3.4.13.min.js +0 -0
  388. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/vendor/markdown-it-14.3.2.LICENSE.txt +0 -0
  389. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/vendor/markdown-it-14.3.2.min.js +0 -0
  390. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/vendor/mermaid-11.16.1.LICENSE.txt +0 -0
  391. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/vendor/mermaid-11.16.1.min.js +0 -0
  392. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/vendor/zxcvbn-4.4.2.LICENSE.txt +0 -0
  393. {metalist-0.6.2 → metalist-0.6.3}/app/static/js/vendor/zxcvbn-4.4.2.js +0 -0
  394. {metalist-0.6.2 → metalist-0.6.3}/app/static/note-html-policy.json +0 -0
  395. {metalist-0.6.2 → metalist-0.6.3}/app/static/video/login-startup.mp4 +0 -0
  396. {metalist-0.6.2 → metalist-0.6.3}/app/templates/base.html +0 -0
  397. {metalist-0.6.2 → metalist-0.6.3}/app/templates/index.html +0 -0
  398. {metalist-0.6.2 → metalist-0.6.3}/app/templates/locked.html +0 -0
  399. {metalist-0.6.2 → metalist-0.6.3}/app/templates/maintenance.html +0 -0
  400. {metalist-0.6.2 → metalist-0.6.3}/app/templates/namespace_deleted.html +0 -0
  401. {metalist-0.6.2 → metalist-0.6.3}/app/templates/namespace_renamed.html +0 -0
  402. {metalist-0.6.2 → metalist-0.6.3}/app/templates/notes_list.html +0 -0
  403. {metalist-0.6.2 → metalist-0.6.3}/app/upload_limits.py +0 -0
  404. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/__init__.py +0 -0
  405. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/add_selected_text_tag.py +0 -0
  406. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/alphabetize_root_notes.py +0 -0
  407. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/base.py +0 -0
  408. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/bulk_tag_proposals.py +0 -0
  409. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/collapse.py +0 -0
  410. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/copy_note.py +0 -0
  411. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/create_child.py +0 -0
  412. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/create_note.py +0 -0
  413. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/create_sibling.py +0 -0
  414. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/delete_subtree.py +0 -0
  415. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/delete_tag.py +0 -0
  416. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/expand.py +0 -0
  417. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/indent.py +0 -0
  418. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/lock.py +0 -0
  419. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/move.py +0 -0
  420. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/move_to_top.py +0 -0
  421. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/outdent.py +0 -0
  422. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/paste_child.py +0 -0
  423. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/paste_sibling.py +0 -0
  424. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/prefetch_link_titles.py +0 -0
  425. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/prioritize.py +0 -0
  426. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/redo.py +0 -0
  427. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/rename_tag.py +0 -0
  428. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/reset_updated_at.py +0 -0
  429. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/resize_image.py +0 -0
  430. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/run_shell.py +0 -0
  431. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/search_comment_autofill.py +0 -0
  432. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/search_context_tags.py +0 -0
  433. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/set_collapse_bulk.py +0 -0
  434. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/set_collapse_in_context.py +0 -0
  435. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/split_note.py +0 -0
  436. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/tag_proposals.py +0 -0
  437. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/toggle_reference_mode.py +0 -0
  438. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/toggle_todo_done.py +0 -0
  439. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/undo.py +0 -0
  440. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/unformat_content.py +0 -0
  441. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/update_content.py +0 -0
  442. {metalist-0.6.2 → metalist-0.6.3}/app/usecases/view.py +0 -0
  443. {metalist-0.6.2 → metalist-0.6.3}/app/utils/__init__.py +0 -0
  444. {metalist-0.6.2 → metalist-0.6.3}/app/utils/encryption.py +0 -0
  445. {metalist-0.6.2 → metalist-0.6.3}/app/utils/text_utils.py +0 -0
  446. {metalist-0.6.2 → metalist-0.6.3}/convert-from-legacy.py +0 -0
  447. {metalist-0.6.2 → metalist-0.6.3}/main.py +0 -0
  448. {metalist-0.6.2 → metalist-0.6.3}/metalist.egg-info/dependency_links.txt +0 -0
  449. {metalist-0.6.2 → metalist-0.6.3}/metalist.egg-info/entry_points.txt +0 -0
  450. {metalist-0.6.2 → metalist-0.6.3}/metalist.egg-info/requires.txt +0 -0
  451. {metalist-0.6.2 → metalist-0.6.3}/metalist.egg-info/top_level.txt +0 -0
  452. {metalist-0.6.2 → metalist-0.6.3}/pyproject.toml +0 -0
  453. {metalist-0.6.2 → metalist-0.6.3}/scripts/generate-lan-cert.sh +0 -0
  454. {metalist-0.6.2 → metalist-0.6.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metalist
3
- Version: 0.6.2
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
@@ -48,6 +48,13 @@ 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
+
51
58
  ## Changes in 0.6.2
52
59
 
53
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.
@@ -122,7 +129,11 @@ uv tool install metalist
122
129
  metalist
123
130
  ```
124
131
 
125
- 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:
126
137
  ```bash
127
138
  metalist update
128
139
  ```
@@ -130,7 +141,7 @@ The updater checks the installed version against the latest PyPI release first.
130
141
 
131
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.
132
143
 
133
- 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`; 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.
134
145
 
135
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.
136
147
 
@@ -12,6 +12,13 @@ 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
+
15
22
  ## Changes in 0.6.2
16
23
 
17
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.
@@ -86,7 +93,11 @@ uv tool install metalist
86
93
  metalist
87
94
  ```
88
95
 
89
- 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:
90
101
  ```bash
91
102
  metalist update
92
103
  ```
@@ -94,7 +105,7 @@ The updater checks the installed version against the latest PyPI release first.
94
105
 
95
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.
96
107
 
97
- 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`; 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.
98
109
 
99
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.
100
111
 
@@ -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,13 @@ 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'),
8
15
  # Release smoke polls only its own disposable HTTP listeners during an actual update.
9
16
  'scripts/smoke_self_update.py:_has_updated_namespace:probe_capture': ('HTTPError',),
10
17
  # A child can exit between poll and terminate; hung children must be killed and reaped.
@@ -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)
@@ -43,6 +43,8 @@ def schedule_self_update(
43
43
  current_pid: int,
44
44
  platform_name: str,
45
45
  environ: Mapping[str, str],
46
+ # lint: allow-PY002 rationale="Preserve the existing CLI scheduler contract; the in-app caller supplies the reviewed release pin"
47
+ expected_target_version: str | None = None,
46
48
  ) -> SelfUpdateScheduleResult:
47
49
  _validate_schedule_inputs(
48
50
  current_version=current_version,
@@ -51,6 +53,10 @@ def schedule_self_update(
51
53
  platform_name=platform_name,
52
54
  )
53
55
  target_version = _fetch_latest_pypi_version()
56
+ if expected_target_version is not None:
57
+ _release_version_key(expected_target_version)
58
+ if target_version != expected_target_version:
59
+ raise RuntimeError('The available release changed; check for updates again. MetaList was not stopped.')
54
60
  current_version_key = _release_version_key(current_version)
55
61
  target_version_key = _release_version_key(target_version)
56
62
  if current_version_key >= target_version_key:
@@ -37,7 +37,7 @@ class ShellCapacityError(ValueError):
37
37
  def _terminate_tree(process) -> None:
38
38
  with CapturedExceptionContext(ProcessLookupError, boundary='app/services/shell_session_service.py:_terminate_tree:capture'):
39
39
  if os.name == 'nt':
40
- stop_windows_process(pid=process.pid)
40
+ stop_windows_process(process=process)
41
41
  else:
42
42
  os.killpg(process.pid, signal.SIGKILL)
43
43
 
@@ -15,6 +15,7 @@ import httpx
15
15
  from app.services.namespace_switcher import NamespaceLaunchProcess
16
16
  from app.services.namespace_switcher import _stop_failed_namespace_launch
17
17
  from app.services.namespace_switcher import _wait_for_namespace_ready
18
+ from app.services.windows_process_control import stop_process_tree as stop_windows_process_tree
18
19
 
19
20
 
20
21
  def _run_checked(command: list[str], *, environ: Mapping[str, str], directory: Path) -> str:
@@ -78,6 +79,10 @@ def _probe_candidate(*, python: str, executable: str, directory: Path,
78
79
  if len(response.content) == 0:
79
80
  raise RuntimeError(f"Update candidate has an empty runtime resource: {path}")
80
81
  finally:
82
+ if sys.platform == "win32":
83
+ # The CLI/venv launchers can own a separate Python server process.
84
+ # Stop descendants even if readiness failure already reaped the launcher.
85
+ stop_windows_process_tree(process=process)
81
86
  _stop_failed_namespace_launch(process=process)
82
87
 
83
88
 
@@ -3,6 +3,8 @@ from __future__ import annotations
3
3
  import shutil
4
4
  import subprocess
5
5
  import time
6
+ import ctypes
7
+ from ctypes import wintypes
6
8
 
7
9
 
8
10
  _WAIT_POLL_INTERVAL_SECONDS = 0.25
@@ -145,20 +147,51 @@ def stop_process(*, pid: int) -> None:
145
147
  raise RuntimeError(f"Timed out waiting for Windows process {pid} to exit")
146
148
 
147
149
 
148
- def stop_process_tree(*, pid: int) -> None:
149
- """Stop a shell and all descendants, including children of an exited parent."""
150
+ def _creation_filetime(process) -> int:
151
+ """Read identity from the owned handle, which survives exit and prevents PID reuse."""
152
+ kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
153
+ get_times = kernel32.GetProcessTimes
154
+ get_times.argtypes = [wintypes.HANDLE, *([ctypes.POINTER(wintypes.FILETIME)] * 4)]
155
+ get_times.restype = wintypes.BOOL
156
+ created, exited, kernel, user = (wintypes.FILETIME() for _ in range(4))
157
+ if not get_times(int(process._handle), ctypes.byref(created), ctypes.byref(exited),
158
+ ctypes.byref(kernel), ctypes.byref(user)):
159
+ raise ctypes.WinError(ctypes.get_last_error())
160
+ return (created.dwHighDateTime << 32) | created.dwLowDateTime
161
+
162
+
163
+ def stop_process_tree(*, process) -> None:
164
+ """Stop and wait for descendants, including children of an exited parent."""
165
+ pid = process.pid
150
166
  _validate_pid(pid=pid)
167
+ created_filetime = _creation_filetime(process)
151
168
  _run_powershell(
152
169
  script=(
170
+ "$ErrorActionPreference = 'Stop'; "
153
171
  "$all = @(Get-CimInstance Win32_Process -ErrorAction Stop); "
154
172
  f"$pending = [System.Collections.Generic.List[int]]::new(); $pending.Add({pid}); "
173
+ f"$created = @{{}}; $created[{pid}] = [datetime]::FromFileTimeUtc({created_filetime}); "
155
174
  "$seen = [System.Collections.Generic.HashSet[int]]::new(); "
156
175
  "for ($i=0; $i -lt $pending.Count; $i++) { "
157
176
  "$parent = $pending[$i]; if (-not $seen.Add($parent)) { continue }; "
158
- "foreach ($child in $all) { if ($child.ParentProcessId -eq $parent) { $pending.Add([int]$child.ProcessId) } } }; "
177
+ "foreach ($child in $all) { if ($child.ParentProcessId -eq $parent) { "
178
+ "if ($null -eq $child.CreationDate) { throw 'Missing child process creation time' }; "
179
+ "$birth = $child.CreationDate.ToUniversalTime(); "
180
+ "if ($birth -ge $created[$parent]) { "
181
+ "$pending.Add([int]$child.ProcessId); $created[[int]$child.ProcessId] = $birth } } } }; "
159
182
  "for ($i=$pending.Count-1; $i -ge 0; $i--) { "
160
183
  "$target = Get-Process -Id $pending[$i] -ErrorAction SilentlyContinue; "
161
- "if ($null -ne $target) { $target | Stop-Process -Force -ErrorAction Stop } }"
184
+ "if ($null -ne $target) { try { "
185
+ "$null = $target.Handle; "
186
+ "if ($target.StartTime.ToUniversalTime().ToString('yyyyMMddHHmmssffffff') -ne "
187
+ "$created[$pending[$i]].ToString('yyyyMMddHHmmssffffff')) { throw 'Process identity changed before cleanup' }; "
188
+ "$target | Stop-Process -Force -ErrorAction Stop; "
189
+ f"if (-not $target.WaitForExit({int(_KILL_GRACE_SECONDS * 1000)})) {{ "
190
+ "throw ('Timed out waiting for process ' + $pending[$i] + ' to exit') } "
191
+ "} finally { $target.Dispose() } } }; "
192
+ # An absent launcher is expected after its child exits. Its final
193
+ # Get-Process lookup can leave $? false despite successful cleanup.
194
+ "exit 0"
162
195
  ),
163
196
  operation=f"stopping process tree {pid}",
164
197
  )
@@ -43,6 +43,9 @@ PY_ALLOWED_EXCEPTION_NAMES = (
43
43
  )
44
44
 
45
45
  JS_ALLOWED_TRY_CALLEE_NAMES = (
46
+ # HTTP update requests and write-through notification preferences; catches rethrow internal bugs.
47
+ "updateRequest",
48
+ "persistUpdateNotice",
46
49
  "fetch",
47
50
  "JSON.parse",
48
51
  "clearAiChatSession",
@@ -118,7 +118,7 @@
118
118
  },
119
119
  {
120
120
  "id": "form.version_info",
121
- "tags": ["version", "versions", "schema", "database", "sqlite", "migration", "migrations", "about", "info", "debug"]
121
+ "tags": ["update", "updates", "upgrade", "release", "pypi", "version", "versions", "schema", "database", "sqlite", "migration", "migrations", "about", "info", "debug"]
122
122
  },
123
123
  {
124
124
  "id": "form.note_layout_appearance",
@@ -8365,3 +8365,19 @@ body.ai-tagging-locked .ai-chat-tag-operation select:enabled { cursor: pointer !
8365
8365
  min-width: 0;
8366
8366
  overflow-wrap: anywhere;
8367
8367
  }
8368
+
8369
+
8370
+ /* Release notices use the reminder card styling without sharing reminder state. */
8371
+ .app-update-notice {
8372
+ position: fixed;
8373
+ right: 18px;
8374
+ bottom: 18px;
8375
+ z-index: 1100;
8376
+ max-width: min(460px, calc(100vw - 36px));
8377
+ display: flex;
8378
+ align-items: center;
8379
+ gap: 12px;
8380
+ }
8381
+ .app-update-notice button { flex-shrink: 0; }
8382
+ .version-update-section { margin-top: 18px; }
8383
+ .version-update-log { overflow-wrap: anywhere; font-size: 0.85em; }