MetaList 0.7.2__tar.gz → 0.7.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (471) hide show
  1. {metalist-0.7.2 → metalist-0.7.4}/PKG-INFO +14 -1
  2. {metalist-0.7.2 → metalist-0.7.4}/README.md +13 -0
  3. {metalist-0.7.2 → metalist-0.7.4}/app/config.py +2 -2
  4. {metalist-0.7.2 → metalist-0.7.4}/app/db/live_recovery.py +4 -1
  5. {metalist-0.7.2 → metalist-0.7.4}/app/https_proxy.py +55 -15
  6. {metalist-0.7.2 → metalist-0.7.4}/app/models/database.py +3 -18
  7. {metalist-0.7.2 → metalist-0.7.4}/app/services/app_updates.py +18 -15
  8. {metalist-0.7.2 → metalist-0.7.4}/app/services/self_update.py +1 -7
  9. metalist-0.7.4/app/services/update_installer.py +260 -0
  10. {metalist-0.7.2 → metalist-0.7.4}/app/services/update_preflight.py +3 -2
  11. {metalist-0.7.2 → metalist-0.7.4}/app/startup_js_sanity.py +1 -1
  12. {metalist-0.7.2 → metalist-0.7.4}/app/startup_sanity.py +1 -1
  13. {metalist-0.7.2 → metalist-0.7.4}/app/version.py +1 -1
  14. {metalist-0.7.2 → metalist-0.7.4}/metalist.egg-info/PKG-INFO +14 -1
  15. {metalist-0.7.2 → metalist-0.7.4}/metalist.egg-info/SOURCES.txt +1 -0
  16. {metalist-0.7.2 → metalist-0.7.4}/MANIFEST.in +0 -0
  17. {metalist-0.7.2 → metalist-0.7.4}/app/__init__.py +0 -0
  18. {metalist-0.7.2 → metalist-0.7.4}/app/api/__init__.py +0 -0
  19. {metalist-0.7.2 → metalist-0.7.4}/app/api/deps.py +0 -0
  20. {metalist-0.7.2 → metalist-0.7.4}/app/api/dev.py +0 -0
  21. {metalist-0.7.2 → metalist-0.7.4}/app/api/middleware/auth.py +0 -0
  22. {metalist-0.7.2 → metalist-0.7.4}/app/api/note_requests.py +0 -0
  23. {metalist-0.7.2 → metalist-0.7.4}/app/api/request_auth.py +0 -0
  24. {metalist-0.7.2 → metalist-0.7.4}/app/api/routes/ai.py +0 -0
  25. {metalist-0.7.2 → metalist-0.7.4}/app/api/routes/app_updates.py +0 -0
  26. {metalist-0.7.2 → metalist-0.7.4}/app/api/routes/auth.py +0 -0
  27. {metalist-0.7.2 → metalist-0.7.4}/app/api/routes/backups.py +0 -0
  28. {metalist-0.7.2 → metalist-0.7.4}/app/api/routes/files.py +0 -0
  29. {metalist-0.7.2 → metalist-0.7.4}/app/api/routes/notes.py +0 -0
  30. {metalist-0.7.2 → metalist-0.7.4}/app/api/routes/ontology.py +0 -0
  31. {metalist-0.7.2 → metalist-0.7.4}/app/api/routes/reminders.py +0 -0
  32. {metalist-0.7.2 → metalist-0.7.4}/app/api/routes/remote_images.py +0 -0
  33. {metalist-0.7.2 → metalist-0.7.4}/app/api/routes/test.py +0 -0
  34. {metalist-0.7.2 → metalist-0.7.4}/app/api/transactions.py +0 -0
  35. {metalist-0.7.2 → metalist-0.7.4}/app/api/upload_limits.py +0 -0
  36. {metalist-0.7.2 → metalist-0.7.4}/app/data_directory.py +0 -0
  37. {metalist-0.7.2 → metalist-0.7.4}/app/db/__init__.py +0 -0
  38. {metalist-0.7.2 → metalist-0.7.4}/app/db/backup_settings_sql.py +0 -0
  39. {metalist-0.7.2 → metalist-0.7.4}/app/db/engine.py +0 -0
  40. {metalist-0.7.2 → metalist-0.7.4}/app/db/file_schema.py +0 -0
  41. {metalist-0.7.2 → metalist-0.7.4}/app/db/file_session.py +0 -0
  42. {metalist-0.7.2 → metalist-0.7.4}/app/db/files_sql.py +0 -0
  43. {metalist-0.7.2 → metalist-0.7.4}/app/db/link_titles_sql.py +0 -0
  44. {metalist-0.7.2 → metalist-0.7.4}/app/db/migrations.py +0 -0
  45. {metalist-0.7.2 → metalist-0.7.4}/app/db/notes_sql.py +0 -0
  46. {metalist-0.7.2 → metalist-0.7.4}/app/db/ontology_rules_sql.py +0 -0
  47. {metalist-0.7.2 → metalist-0.7.4}/app/db/reminders_sql.py +0 -0
  48. {metalist-0.7.2 → metalist-0.7.4}/app/db/retire_sounds.py +0 -0
  49. {metalist-0.7.2 → metalist-0.7.4}/app/db/schema.py +0 -0
  50. {metalist-0.7.2 → metalist-0.7.4}/app/db/search_history_sql.py +0 -0
  51. {metalist-0.7.2 → metalist-0.7.4}/app/db/session.py +0 -0
  52. {metalist-0.7.2 → metalist-0.7.4}/app/db/settings_sql.py +0 -0
  53. {metalist-0.7.2 → metalist-0.7.4}/app/db/tab_state_sql.py +0 -0
  54. {metalist-0.7.2 → metalist-0.7.4}/app/db/version.py +0 -0
  55. {metalist-0.7.2 → metalist-0.7.4}/app/encryption_audit.py +0 -0
  56. {metalist-0.7.2 → metalist-0.7.4}/app/exception_boundaries.py +0 -0
  57. {metalist-0.7.2 → metalist-0.7.4}/app/main.py +0 -0
  58. {metalist-0.7.2 → metalist-0.7.4}/app/models/commands.py +0 -0
  59. {metalist-0.7.2 → metalist-0.7.4}/app/models/entities.py +0 -0
  60. {metalist-0.7.2 → metalist-0.7.4}/app/models/enums.py +0 -0
  61. {metalist-0.7.2 → metalist-0.7.4}/app/models/linked_list.py +0 -0
  62. {metalist-0.7.2 → metalist-0.7.4}/app/models/list_operations.py +0 -0
  63. {metalist-0.7.2 → metalist-0.7.4}/app/models/list_traversal.py +0 -0
  64. {metalist-0.7.2 → metalist-0.7.4}/app/models/note_crud.py +0 -0
  65. {metalist-0.7.2 → metalist-0.7.4}/app/models/utils.py +0 -0
  66. {metalist-0.7.2 → metalist-0.7.4}/app/network_settings.py +0 -0
  67. {metalist-0.7.2 → metalist-0.7.4}/app/presentation/__init__.py +0 -0
  68. {metalist-0.7.2 → metalist-0.7.4}/app/presentation/render/__init__.py +0 -0
  69. {metalist-0.7.2 → metalist-0.7.4}/app/presentation/render/note_renderer.py +0 -0
  70. {metalist-0.7.2 → metalist-0.7.4}/app/presentation/templates.py +0 -0
  71. {metalist-0.7.2 → metalist-0.7.4}/app/security/authenticated_logging.py +0 -0
  72. {metalist-0.7.2 → metalist-0.7.4}/app/security/encryption.py +0 -0
  73. {metalist-0.7.2 → metalist-0.7.4}/app/security/http_headers.py +0 -0
  74. {metalist-0.7.2 → metalist-0.7.4}/app/security/note_html.py +0 -0
  75. {metalist-0.7.2 → metalist-0.7.4}/app/security/request_boundary.py +0 -0
  76. {metalist-0.7.2 → metalist-0.7.4}/app/security/request_hosts.py +0 -0
  77. {metalist-0.7.2 → metalist-0.7.4}/app/security/sensitive_cache.py +0 -0
  78. {metalist-0.7.2 → metalist-0.7.4}/app/security/sensitive_logging.py +0 -0
  79. {metalist-0.7.2 → metalist-0.7.4}/app/security/shell_execution.py +0 -0
  80. {metalist-0.7.2 → metalist-0.7.4}/app/security/validation_errors.py +0 -0
  81. {metalist-0.7.2 → metalist-0.7.4}/app/server_runtime.py +0 -0
  82. {metalist-0.7.2 → metalist-0.7.4}/app/services/__init__.py +0 -0
  83. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/__init__.py +0 -0
  84. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/actions.py +0 -0
  85. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/cloud_privacy.py +0 -0
  86. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/context.py +0 -0
  87. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/evidence_serialization.py +0 -0
  88. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/help_catalog.py +0 -0
  89. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/history.py +0 -0
  90. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/inference.py +0 -0
  91. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/investigation.py +0 -0
  92. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/judging.py +0 -0
  93. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/menu_actions.py +0 -0
  94. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/model_policy.py +0 -0
  95. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/openai_cost_tracking.py +0 -0
  96. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/openai_inference.py +0 -0
  97. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/permissions.py +0 -0
  98. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/prompt_settings.py +0 -0
  99. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/prompts/__init__.py +0 -0
  100. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/prompts/final-response.md +0 -0
  101. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/prompts/help-response.md +0 -0
  102. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/prompts/system.md +0 -0
  103. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/prompts/tool-result.md +0 -0
  104. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/retrieval_settings.py +0 -0
  105. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/runtime.py +0 -0
  106. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/scope.py +0 -0
  107. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skill_settings.py +0 -0
  108. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/__init__.py +0 -0
  109. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/help-ai.md +0 -0
  110. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/help-data.md +0 -0
  111. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/help-formatting.md +0 -0
  112. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/help-menus.md +0 -0
  113. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/help-notes.md +0 -0
  114. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/help-privacy.md +0 -0
  115. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/help-references.md +0 -0
  116. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/help-reminders.md +0 -0
  117. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/help-search.md +0 -0
  118. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/help-tags.md +0 -0
  119. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/skills/scoped-investigation.md +0 -0
  120. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/structured_inference.py +0 -0
  121. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/tagging.py +0 -0
  122. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/tagging_run.py +0 -0
  123. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/token_estimation.py +0 -0
  124. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/tools.py +0 -0
  125. {metalist-0.7.2 → metalist-0.7.4}/app/services/agent/trace.py +0 -0
  126. {metalist-0.7.2 → metalist-0.7.4}/app/services/ai_chat.py +0 -0
  127. {metalist-0.7.2 → metalist-0.7.4}/app/services/ai_chat_rendering.py +0 -0
  128. {metalist-0.7.2 → metalist-0.7.4}/app/services/ai_chat_stream.py +0 -0
  129. {metalist-0.7.2 → metalist-0.7.4}/app/services/app_update_worker.py +0 -0
  130. {metalist-0.7.2 → metalist-0.7.4}/app/services/auth.py +0 -0
  131. {metalist-0.7.2 → metalist-0.7.4}/app/services/auth_cache_state.py +0 -0
  132. {metalist-0.7.2 → metalist-0.7.4}/app/services/auth_service.py +0 -0
  133. {metalist-0.7.2 → metalist-0.7.4}/app/services/backlink_index.py +0 -0
  134. {metalist-0.7.2 → metalist-0.7.4}/app/services/backlinks.py +0 -0
  135. {metalist-0.7.2 → metalist-0.7.4}/app/services/backup_service.py +0 -0
  136. {metalist-0.7.2 → metalist-0.7.4}/app/services/backup_settings_service.py +0 -0
  137. {metalist-0.7.2 → metalist-0.7.4}/app/services/bulk_operation.py +0 -0
  138. {metalist-0.7.2 → metalist-0.7.4}/app/services/client_state_service.py +0 -0
  139. {metalist-0.7.2 → metalist-0.7.4}/app/services/content_cache.py +0 -0
  140. {metalist-0.7.2 → metalist-0.7.4}/app/services/content_formatting.py +0 -0
  141. {metalist-0.7.2 → metalist-0.7.4}/app/services/diagnostics.py +0 -0
  142. {metalist-0.7.2 → metalist-0.7.4}/app/services/embedded_references.py +0 -0
  143. {metalist-0.7.2 → metalist-0.7.4}/app/services/encryption.py +0 -0
  144. {metalist-0.7.2 → metalist-0.7.4}/app/services/exception_capture.py +0 -0
  145. {metalist-0.7.2 → metalist-0.7.4}/app/services/file_registry.py +0 -0
  146. {metalist-0.7.2 → metalist-0.7.4}/app/services/file_storage.py +0 -0
  147. {metalist-0.7.2 → metalist-0.7.4}/app/services/footnote_layout.py +0 -0
  148. {metalist-0.7.2 → metalist-0.7.4}/app/services/footnote_rendering.py +0 -0
  149. {metalist-0.7.2 → metalist-0.7.4}/app/services/hierarchy.py +0 -0
  150. {metalist-0.7.2 → metalist-0.7.4}/app/services/html_export.py +0 -0
  151. {metalist-0.7.2 → metalist-0.7.4}/app/services/html_unformatting.py +0 -0
  152. {metalist-0.7.2 → metalist-0.7.4}/app/services/hydration_state.py +0 -0
  153. {metalist-0.7.2 → metalist-0.7.4}/app/services/image_size_formatting.py +0 -0
  154. {metalist-0.7.2 → metalist-0.7.4}/app/services/inline_image_occurrences.py +0 -0
  155. {metalist-0.7.2 → metalist-0.7.4}/app/services/input_errors.py +0 -0
  156. {metalist-0.7.2 → metalist-0.7.4}/app/services/integrity.py +0 -0
  157. {metalist-0.7.2 → metalist-0.7.4}/app/services/latex_rendering.py +0 -0
  158. {metalist-0.7.2 → metalist-0.7.4}/app/services/link_titles.py +0 -0
  159. {metalist-0.7.2 → metalist-0.7.4}/app/services/login_rate_limit.py +0 -0
  160. {metalist-0.7.2 → metalist-0.7.4}/app/services/maintenance_mode.py +0 -0
  161. {metalist-0.7.2 → metalist-0.7.4}/app/services/markdown_rendering.py +0 -0
  162. {metalist-0.7.2 → metalist-0.7.4}/app/services/namespace_deletion_jobs.py +0 -0
  163. {metalist-0.7.2 → metalist-0.7.4}/app/services/namespace_deletion_worker.py +0 -0
  164. {metalist-0.7.2 → metalist-0.7.4}/app/services/namespace_rename_jobs.py +0 -0
  165. {metalist-0.7.2 → metalist-0.7.4}/app/services/namespace_rename_worker.py +0 -0
  166. {metalist-0.7.2 → metalist-0.7.4}/app/services/namespace_runtime_guard.py +0 -0
  167. {metalist-0.7.2 → metalist-0.7.4}/app/services/namespace_switcher.py +0 -0
  168. {metalist-0.7.2 → metalist-0.7.4}/app/services/note_fullscreen.py +0 -0
  169. {metalist-0.7.2 → metalist-0.7.4}/app/services/note_image_tags.py +0 -0
  170. {metalist-0.7.2 → metalist-0.7.4}/app/services/note_ordering.py +0 -0
  171. {metalist-0.7.2 → metalist-0.7.4}/app/services/note_store.py +0 -0
  172. {metalist-0.7.2 → metalist-0.7.4}/app/services/ontology_rules_store.py +0 -0
  173. {metalist-0.7.2 → metalist-0.7.4}/app/services/openai_credentials.py +0 -0
  174. {metalist-0.7.2 → metalist-0.7.4}/app/services/password_note_fields.py +0 -0
  175. {metalist-0.7.2 → metalist-0.7.4}/app/services/reference_presentation.py +0 -0
  176. {metalist-0.7.2 → metalist-0.7.4}/app/services/reminders.py +0 -0
  177. {metalist-0.7.2 → metalist-0.7.4}/app/services/remote_image_proxy.py +0 -0
  178. {metalist-0.7.2 → metalist-0.7.4}/app/services/request_recovery.py +0 -0
  179. {metalist-0.7.2 → metalist-0.7.4}/app/services/resource_limits.py +0 -0
  180. {metalist-0.7.2 → metalist-0.7.4}/app/services/root_sorting.py +0 -0
  181. {metalist-0.7.2 → metalist-0.7.4}/app/services/runtime_generation.py +0 -0
  182. {metalist-0.7.2 → metalist-0.7.4}/app/services/runtime_hardening.py +0 -0
  183. {metalist-0.7.2 → metalist-0.7.4}/app/services/runtime_lock.py +0 -0
  184. {metalist-0.7.2 → metalist-0.7.4}/app/services/search_history.py +0 -0
  185. {metalist-0.7.2 → metalist-0.7.4}/app/services/search_history_storage.py +0 -0
  186. {metalist-0.7.2 → metalist-0.7.4}/app/services/search_index.py +0 -0
  187. {metalist-0.7.2 → metalist-0.7.4}/app/services/search_query.py +0 -0
  188. {metalist-0.7.2 → metalist-0.7.4}/app/services/search_text.py +0 -0
  189. {metalist-0.7.2 → metalist-0.7.4}/app/services/selected_text_tag.py +0 -0
  190. {metalist-0.7.2 → metalist-0.7.4}/app/services/session_timeout_service.py +0 -0
  191. {metalist-0.7.2 → metalist-0.7.4}/app/services/shell_session_service.py +0 -0
  192. {metalist-0.7.2 → metalist-0.7.4}/app/services/snapshot.py +0 -0
  193. {metalist-0.7.2 → metalist-0.7.4}/app/services/store.py +0 -0
  194. {metalist-0.7.2 → metalist-0.7.4}/app/services/structured_note_renderers.py +0 -0
  195. {metalist-0.7.2 → metalist-0.7.4}/app/services/sync.py +0 -0
  196. {metalist-0.7.2 → metalist-0.7.4}/app/services/sync_state.py +0 -0
  197. {metalist-0.7.2 → metalist-0.7.4}/app/services/tab_state.py +0 -0
  198. {metalist-0.7.2 → metalist-0.7.4}/app/services/tag_case.py +0 -0
  199. {metalist-0.7.2 → metalist-0.7.4}/app/services/tag_ontology.py +0 -0
  200. {metalist-0.7.2 → metalist-0.7.4}/app/services/tag_rename.py +0 -0
  201. {metalist-0.7.2 → metalist-0.7.4}/app/services/tag_suggestions.py +0 -0
  202. {metalist-0.7.2 → metalist-0.7.4}/app/services/tag_term_matching.py +0 -0
  203. {metalist-0.7.2 → metalist-0.7.4}/app/services/test_reset.py +0 -0
  204. {metalist-0.7.2 → metalist-0.7.4}/app/services/tokens.py +0 -0
  205. {metalist-0.7.2 → metalist-0.7.4}/app/services/undo_state.py +0 -0
  206. {metalist-0.7.2 → metalist-0.7.4}/app/services/update_backups.py +0 -0
  207. {metalist-0.7.2 → metalist-0.7.4}/app/services/view_cache.py +0 -0
  208. {metalist-0.7.2 → metalist-0.7.4}/app/services/view_diff.py +0 -0
  209. {metalist-0.7.2 → metalist-0.7.4}/app/services/view_state.py +0 -0
  210. {metalist-0.7.2 → metalist-0.7.4}/app/services/windows_process_control.py +0 -0
  211. {metalist-0.7.2 → metalist-0.7.4}/app/startup_environment.py +0 -0
  212. {metalist-0.7.2 → metalist-0.7.4}/app/startup_sanity_config.py +0 -0
  213. {metalist-0.7.2 → metalist-0.7.4}/app/static/config/agent-menu-actions.json +0 -0
  214. {metalist-0.7.2 → metalist-0.7.4}/app/static/config/command_palette_tags.json +0 -0
  215. {metalist-0.7.2 → metalist-0.7.4}/app/static/css/floating-notes.css +0 -0
  216. {metalist-0.7.2 → metalist-0.7.4}/app/static/css/forms.css +0 -0
  217. {metalist-0.7.2 → metalist-0.7.4}/app/static/css/main.css +0 -0
  218. {metalist-0.7.2 → metalist-0.7.4}/app/static/img/favicon.ico +0 -0
  219. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/locked.js +0 -0
  220. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/main.js +0 -0
  221. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/activity-tracker.js +0 -0
  222. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/agent-menu-actions.js +0 -0
  223. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/agent-prompt-service.js +0 -0
  224. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/agent-retrieval-settings.js +0 -0
  225. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/ai-agent-debug-view.js +0 -0
  226. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/ai-chat-api.js +0 -0
  227. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/ai-chat-panel-controller.js +0 -0
  228. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/ai-chat-panel-service.js +0 -0
  229. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/ai-thinking-level-service.js +0 -0
  230. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/bulk-elapsed-clock.js +0 -0
  231. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/bulk-proposal-ui.js +0 -0
  232. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/cloud-privacy-policy.js +0 -0
  233. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/ai-chat/proposal-menu.js +0 -0
  234. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/api-client.js +0 -0
  235. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/api-failure-classification-service.js +0 -0
  236. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/app-update-service.js +0 -0
  237. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/application-state.js +0 -0
  238. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/async-result.js +0 -0
  239. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/auth.js +0 -0
  240. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/client-state-api.js +0 -0
  241. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/client-state-migration.js +0 -0
  242. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/command-palette/activation-auth-policy.js +0 -0
  243. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/command-palette/backup-command-availability.js +0 -0
  244. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/command-palette/click-activation-service.js +0 -0
  245. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/command-palette/command-palette-controller.js +0 -0
  246. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/command-palette/endpoint-registry.js +0 -0
  247. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/command-palette/note-layout-preferences.js +0 -0
  248. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/command-palette/preferences-store.js +0 -0
  249. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/command-palette/tag-config-loader.js +0 -0
  250. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/command-palette/usage-store.js +0 -0
  251. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/config.js +0 -0
  252. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/connectivity-monitor.js +0 -0
  253. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/context-menu/context-menu-registry.js +0 -0
  254. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/context-menu/context-menu-service.js +0 -0
  255. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/context-menu/context-menu-target-service.js +0 -0
  256. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/context-menu/input-caret-service.js +0 -0
  257. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/disconnect-editing-state-service.js +0 -0
  258. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/dom-utils-fixed.js +0 -0
  259. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/dom-utils.js +0 -0
  260. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/editor-commands.js +0 -0
  261. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/editor-selection.js +0 -0
  262. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/editor-toolbar.js +0 -0
  263. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/elapsed-time.js +0 -0
  264. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/error-handler.js +0 -0
  265. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/error-overlay.js +0 -0
  266. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/expected-errors.js +0 -0
  267. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/location-flags.js +0 -0
  268. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/login-namespace-picker.js +0 -0
  269. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/maintenance.js +0 -0
  270. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/agent-prompt-editor-modal.js +0 -0
  271. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/ai-agent-settings-modal.js +0 -0
  272. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/alphabetize-root-notes-modal.js +0 -0
  273. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/backup-restore-modal.js +0 -0
  274. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/backup-result-modal.js +0 -0
  275. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/backup-retention-modal.js +0 -0
  276. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/backup-settings-modal.js +0 -0
  277. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/base-modal.js +0 -0
  278. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/confirmation-modal.js +0 -0
  279. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/delete-namespace-modal.js +0 -0
  280. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/delete-namespace-validation.js +0 -0
  281. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/help-modal.js +0 -0
  282. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/namespace-loading-page.js +0 -0
  283. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/namespace-modals.js +0 -0
  284. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/namespace-ports-profile.js +0 -0
  285. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/namespace-rename-validation.js +0 -0
  286. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/note-layout-appearance-modal.js +0 -0
  287. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/ontology-modal.js +0 -0
  288. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/password-modal.js +0 -0
  289. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/prioritize-modal.js +0 -0
  290. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/random-password-modal.js +0 -0
  291. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/reminder-modal.js +0 -0
  292. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/reset-updated-at-modal.js +0 -0
  293. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/search-suggestion-statistics-modal.js +0 -0
  294. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/session-timeout-modal.js +0 -0
  295. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/modals/version-info-modal.js +0 -0
  296. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/actions/content-actions.js +0 -0
  297. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/actions/history-actions.js +0 -0
  298. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/actions/note-actions.js +0 -0
  299. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/actions/search-actions.js +0 -0
  300. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/actions/selection-actions.js +0 -0
  301. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/actions/ui-actions.js +0 -0
  302. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/events/context-menu-events.js +0 -0
  303. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/events/focus-events.js +0 -0
  304. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/events/inactivity-events.js +0 -0
  305. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/events/input-events.js +0 -0
  306. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/events/keyboard-events.js +0 -0
  307. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/events/mouse-events.js +0 -0
  308. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/events/search-events.js +0 -0
  309. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/mode-context.js +0 -0
  310. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/mode-logger.js +0 -0
  311. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/mode-manager-controller.js +0 -0
  312. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/add-style-service.js +0 -0
  313. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/animated-scroll-service.js +0 -0
  314. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/backlinks-navigation-service.js +0 -0
  315. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/backlinks-panel-service.js +0 -0
  316. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/clipboard-shortcut-policy-service.js +0 -0
  317. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/collapse-affordance-service.js +0 -0
  318. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/collapse-editing-policy-service.js +0 -0
  319. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/command-gate-service.js +0 -0
  320. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/context-menu-target-service.js +0 -0
  321. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/deselect-selection-state-service.js +0 -0
  322. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/differential-view-service.js +0 -0
  323. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/edit-session-collapse-policy-service.js +0 -0
  324. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/edit-session-collapse-service.js +0 -0
  325. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/embedded-image-service.js +0 -0
  326. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/file-drop-policy-service.js +0 -0
  327. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/file-image-preview-service.js +0 -0
  328. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/file-reference-service.js +0 -0
  329. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/filtered-refresh-selection-service.js +0 -0
  330. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/floating-note-dom.js +0 -0
  331. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/floating-note-layout.js +0 -0
  332. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/floating-note-service.js +0 -0
  333. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/footnote-navigation-service.js +0 -0
  334. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/history-selection-policy-service.js +0 -0
  335. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/history-shortcut-policy-service.js +0 -0
  336. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/html-paste-sanitizer-service.js +0 -0
  337. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/image-context-menu-action-service.js +0 -0
  338. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/image-file-insert-choice-modal-service.js +0 -0
  339. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/infinite-scroll-service.js +0 -0
  340. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/link-context-menu-action-service.js +0 -0
  341. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/link-title-prefetch-service.js +0 -0
  342. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/markdown-render-service.js +0 -0
  343. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/mermaid-render-service.js +0 -0
  344. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/note-click-target-service.js +0 -0
  345. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/note-clipboard-write-service.js +0 -0
  346. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/note-collapse-animation-service.js +0 -0
  347. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/note-context-menu-action-service.js +0 -0
  348. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/note-delete-shortcut-policy-service.js +0 -0
  349. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/note-drag-service.js +0 -0
  350. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/note-fullscreen-service.js +0 -0
  351. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/note-reposition-animation-service.js +0 -0
  352. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/note-split-service.js +0 -0
  353. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/note-timestamp-hover-service.js +0 -0
  354. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/password-clipboard-service.js +0 -0
  355. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/polling-service.js +0 -0
  356. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/reference-source-navigation-service.js +0 -0
  357. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/remote-image-proxy-service.js +0 -0
  358. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/remove-formatting-service.js +0 -0
  359. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/root-date-separator-service.js +0 -0
  360. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/root-sort-indicator-service.js +0 -0
  361. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/root-sort-service.js +0 -0
  362. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/scroll-anchor-service.js +0 -0
  363. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/scroll-restoration-service.js +0 -0
  364. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/scroll-to-top-service.js +0 -0
  365. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/search-contexts-overlay-service.js +0 -0
  366. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/search-debounce-service.js +0 -0
  367. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/search-input-service.js +0 -0
  368. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/search-interaction-service.js +0 -0
  369. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/search-redaction-reveal-service.js +0 -0
  370. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/search-suggestion-statistics-service.js +0 -0
  371. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/search-suggestion-window-labels-service.js +0 -0
  372. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/search-suggestion-windows-service.js +0 -0
  373. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/search-suggestions-service.js +0 -0
  374. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/search-syntax-service.js +0 -0
  375. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/selected-formatting-removal-service.js +0 -0
  376. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/selected-text-tag-service.js +0 -0
  377. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/shell-output-service.js +0 -0
  378. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/tab-dom-cache-service.js +0 -0
  379. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/tab-drag-service.js +0 -0
  380. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/tab-duplication-service.js +0 -0
  381. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/tab-state-service.js +0 -0
  382. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/tag-bar-service.js +0 -0
  383. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/tag-input-selection-service.js +0 -0
  384. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/tag-proposal-service.js +0 -0
  385. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/tag-suggestions-service.js +0 -0
  386. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/tag-syntax-service.js +0 -0
  387. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/todo-toggle-editing-service.js +0 -0
  388. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/untagged-view-indicator-service.js +0 -0
  389. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/mode-manager/services/view-mode-search-shortcut-service.js +0 -0
  390. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/note-html-sanitizer.js +0 -0
  391. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/password-generator.js +0 -0
  392. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/password-operation-response.js +0 -0
  393. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/password-policy.js +0 -0
  394. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/password-strength.js +0 -0
  395. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/reminder-store.js +0 -0
  396. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/reminder-surface-service.js +0 -0
  397. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/selection-markers.js +0 -0
  398. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/session-auth.js +0 -0
  399. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/tag-token.js +0 -0
  400. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/modules/uuid.js +0 -0
  401. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/namespace-deleted.js +0 -0
  402. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/namespace-renamed.js +0 -0
  403. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/vendor/dompurify-3.4.13.LICENSE.txt +0 -0
  404. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/vendor/dompurify-3.4.13.min.js +0 -0
  405. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/vendor/markdown-it-14.3.2.LICENSE.txt +0 -0
  406. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/vendor/markdown-it-14.3.2.min.js +0 -0
  407. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/vendor/mermaid-11.16.1.LICENSE.txt +0 -0
  408. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/vendor/mermaid-11.16.1.min.js +0 -0
  409. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/vendor/zxcvbn-4.4.2.LICENSE.txt +0 -0
  410. {metalist-0.7.2 → metalist-0.7.4}/app/static/js/vendor/zxcvbn-4.4.2.js +0 -0
  411. {metalist-0.7.2 → metalist-0.7.4}/app/static/note-html-policy.json +0 -0
  412. {metalist-0.7.2 → metalist-0.7.4}/app/static/video/login-startup.mp4 +0 -0
  413. {metalist-0.7.2 → metalist-0.7.4}/app/templates/base.html +0 -0
  414. {metalist-0.7.2 → metalist-0.7.4}/app/templates/index.html +0 -0
  415. {metalist-0.7.2 → metalist-0.7.4}/app/templates/locked.html +0 -0
  416. {metalist-0.7.2 → metalist-0.7.4}/app/templates/maintenance.html +0 -0
  417. {metalist-0.7.2 → metalist-0.7.4}/app/templates/namespace_deleted.html +0 -0
  418. {metalist-0.7.2 → metalist-0.7.4}/app/templates/namespace_renamed.html +0 -0
  419. {metalist-0.7.2 → metalist-0.7.4}/app/templates/notes_list.html +0 -0
  420. {metalist-0.7.2 → metalist-0.7.4}/app/upload_limits.py +0 -0
  421. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/__init__.py +0 -0
  422. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/add_selected_text_tag.py +0 -0
  423. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/alphabetize_root_notes.py +0 -0
  424. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/base.py +0 -0
  425. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/bulk_tag_proposals.py +0 -0
  426. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/collapse.py +0 -0
  427. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/copy_note.py +0 -0
  428. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/create_child.py +0 -0
  429. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/create_note.py +0 -0
  430. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/create_sibling.py +0 -0
  431. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/delete_subtree.py +0 -0
  432. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/delete_tag.py +0 -0
  433. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/expand.py +0 -0
  434. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/indent.py +0 -0
  435. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/lock.py +0 -0
  436. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/move.py +0 -0
  437. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/move_to_top.py +0 -0
  438. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/outdent.py +0 -0
  439. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/paste_child.py +0 -0
  440. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/paste_sibling.py +0 -0
  441. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/prefetch_link_titles.py +0 -0
  442. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/prioritize.py +0 -0
  443. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/redo.py +0 -0
  444. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/rename_tag.py +0 -0
  445. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/reset_updated_at.py +0 -0
  446. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/resize_image.py +0 -0
  447. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/run_shell.py +0 -0
  448. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/search_comment_autofill.py +0 -0
  449. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/search_context_tags.py +0 -0
  450. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/set_collapse_bulk.py +0 -0
  451. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/set_collapse_in_context.py +0 -0
  452. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/split_note.py +0 -0
  453. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/tag_proposals.py +0 -0
  454. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/toggle_reference_mode.py +0 -0
  455. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/toggle_todo_done.py +0 -0
  456. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/undo.py +0 -0
  457. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/unformat_content.py +0 -0
  458. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/update_content.py +0 -0
  459. {metalist-0.7.2 → metalist-0.7.4}/app/usecases/view.py +0 -0
  460. {metalist-0.7.2 → metalist-0.7.4}/app/utils/__init__.py +0 -0
  461. {metalist-0.7.2 → metalist-0.7.4}/app/utils/encryption.py +0 -0
  462. {metalist-0.7.2 → metalist-0.7.4}/app/utils/text_utils.py +0 -0
  463. {metalist-0.7.2 → metalist-0.7.4}/convert-from-legacy.py +0 -0
  464. {metalist-0.7.2 → metalist-0.7.4}/main.py +0 -0
  465. {metalist-0.7.2 → metalist-0.7.4}/metalist.egg-info/dependency_links.txt +0 -0
  466. {metalist-0.7.2 → metalist-0.7.4}/metalist.egg-info/entry_points.txt +0 -0
  467. {metalist-0.7.2 → metalist-0.7.4}/metalist.egg-info/requires.txt +0 -0
  468. {metalist-0.7.2 → metalist-0.7.4}/metalist.egg-info/top_level.txt +0 -0
  469. {metalist-0.7.2 → metalist-0.7.4}/pyproject.toml +0 -0
  470. {metalist-0.7.2 → metalist-0.7.4}/scripts/generate-lan-cert.sh +0 -0
  471. {metalist-0.7.2 → metalist-0.7.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metalist
3
- Version: 0.7.2
3
+ Version: 0.7.4
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,17 @@ 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.7.4
52
+
53
+ - Updates use a private, checksum-verified uv release on Windows, macOS, and Linux. The updater does not execute a user-installed uv, and it revalidates both the cached official archive and extracted executable before each use.
54
+ - The HTTPS listener retries one bodyless safe request after a transient backend reset, records final transport failures server-side, and never replays a mutation or request body.
55
+ - Release CI builds one distribution pair, starts independent platform/Python, installed-update, browser-smoke, and browser-soak gates in parallel, and caches only hash- or lockfile-verified dependencies. Cross-platform runtime changes must pass branch CI before merge.
56
+
57
+ ## Changes in 0.7.3
58
+
59
+ - Windows updates use a private verified uv release when an older installer is vulnerable to the reported PE-resource access-denied failure. A standalone repair ZIP can bootstrap installations whose existing updater cannot update itself.
60
+ - Release candidates now run the complete test and installed-package matrix on Windows, macOS, and Linux across Python 3.10–3.14. Real Chrome and Firefox exercise repeated cold loads and encrypted login on every operating system; Windows also checks Edge over verified LAN HTTPS and the repair path from a published older release.
61
+
51
62
  ## Changes in 0.7.2
52
63
 
53
64
  - Login, hydration, and workspace requests share one initialized browser-tab identity. Losing the `sessionStorage` copy while opening the workspace no longer causes the reported missing-tab-ID crash. The external trigger for that storage loss remains unconfirmed.
@@ -147,6 +158,8 @@ metalist update
147
158
  ```
148
159
  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.
149
160
 
161
+ MetaList downloads its own checksum-verified uv 0.12.17 archive for the current operating system, architecture, and Linux C library. The verified archive and extracted executable are stored in the operating system's user cache; every update verifies the archive against its pinned upstream SHA-256 and the executable against the verified archive before use. That same executable performs both preflight and final installation. MetaList does not execute or replace the user's global uv. Download, verification, platform-selection, or version failures occur before any namespace is stopped. Already-installed releases retain their own updater code; the standalone repair launcher described in [recovery](docs/security/recovery.md#windows-updater-pe-resource-failure) supplies this managed installer to an older Windows updater.
162
+
150
163
  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.
151
164
 
152
165
  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.
@@ -12,6 +12,17 @@ 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.7.4
16
+
17
+ - Updates use a private, checksum-verified uv release on Windows, macOS, and Linux. The updater does not execute a user-installed uv, and it revalidates both the cached official archive and extracted executable before each use.
18
+ - The HTTPS listener retries one bodyless safe request after a transient backend reset, records final transport failures server-side, and never replays a mutation or request body.
19
+ - Release CI builds one distribution pair, starts independent platform/Python, installed-update, browser-smoke, and browser-soak gates in parallel, and caches only hash- or lockfile-verified dependencies. Cross-platform runtime changes must pass branch CI before merge.
20
+
21
+ ## Changes in 0.7.3
22
+
23
+ - Windows updates use a private verified uv release when an older installer is vulnerable to the reported PE-resource access-denied failure. A standalone repair ZIP can bootstrap installations whose existing updater cannot update itself.
24
+ - Release candidates now run the complete test and installed-package matrix on Windows, macOS, and Linux across Python 3.10–3.14. Real Chrome and Firefox exercise repeated cold loads and encrypted login on every operating system; Windows also checks Edge over verified LAN HTTPS and the repair path from a published older release.
25
+
15
26
  ## Changes in 0.7.2
16
27
 
17
28
  - Login, hydration, and workspace requests share one initialized browser-tab identity. Losing the `sessionStorage` copy while opening the workspace no longer causes the reported missing-tab-ID crash. The external trigger for that storage loss remains unconfirmed.
@@ -111,6 +122,8 @@ metalist update
111
122
  ```
112
123
  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.
113
124
 
125
+ MetaList downloads its own checksum-verified uv 0.12.17 archive for the current operating system, architecture, and Linux C library. The verified archive and extracted executable are stored in the operating system's user cache; every update verifies the archive against its pinned upstream SHA-256 and the executable against the verified archive before use. That same executable performs both preflight and final installation. MetaList does not execute or replace the user's global uv. Download, verification, platform-selection, or version failures occur before any namespace is stopped. Already-installed releases retain their own updater code; the standalone repair launcher described in [recovery](docs/security/recovery.md#windows-updater-pe-resource-failure) supplies this managed installer to an older Windows updater.
126
+
114
127
  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.
115
128
 
116
129
  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.
@@ -114,9 +114,9 @@ if TEST_MODE:
114
114
  # Delete existing test.db if it exists
115
115
  if test_database_path.exists():
116
116
  test_database_path.unlink()
117
- print("🧪 Deleted existing test.db")
117
+ print("Deleted existing test.db")
118
118
 
119
- print("🧪 Running in TEST MODE - using test.db")
119
+ print("Running in TEST MODE - using test.db")
120
120
  else:
121
121
  # Use production database
122
122
  DEFAULT_DB_PATH = str(resolve_default_database_path())
@@ -44,7 +44,10 @@ def _sync_directory(path: Path) -> None:
44
44
 
45
45
 
46
46
  def _sync_file(path: Path) -> None:
47
- with path.open('rb') as handle:
47
+ # Windows' CRT rejects fsync/_commit on a read-only descriptor with EBADF.
48
+ # These are live or recovery files owned by this transaction, so open them
49
+ # without truncation using a descriptor that every supported OS can flush.
50
+ with path.open('r+b') as handle:
48
51
  os.fsync(handle.fileno())
49
52
 
50
53
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  import http.client
4
4
  from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
5
+ import logging
5
6
  from threading import BoundedSemaphore
6
7
  import time
7
8
  from app.upload_limits import MAX_UPLOAD_REQUEST_BYTES
@@ -12,6 +13,8 @@ IDLE_TIMEOUT_SECONDS = 60
12
13
  MAX_REQUEST_SECONDS = 30 * 60
13
14
  HOP_HEADERS = frozenset({'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',
14
15
  'te', 'trailer', 'trailers', 'transfer-encoding', 'upgrade'})
16
+ RETRYABLE_METHODS = frozenset({'GET', 'HEAD', 'OPTIONS'})
17
+ LOGGER = logging.getLogger(__name__)
15
18
 
16
19
 
17
20
  class BoundedProxyServer(ThreadingHTTPServer):
@@ -48,6 +51,39 @@ def make_proxy_handler(*, backend_host: str, backend_port: int, forward_headers)
48
51
  class ProxyHandler(BaseHTTPRequestHandler):
49
52
  protocol_version = 'HTTP/1.1'
50
53
 
54
+ def _request_upstream(self, *, headers, content_length: int, deadline: float):
55
+ attempts = 1
56
+ if self.command in RETRYABLE_METHODS and content_length == 0:
57
+ attempts = 2
58
+ for attempt in range(attempts):
59
+ connection = http.client.HTTPConnection(
60
+ backend_host, backend_port, timeout=IDLE_TIMEOUT_SECONDS,
61
+ )
62
+ # lint: allow-PY001 rationale="retry a bodyless safe request after an expected transient upstream transport failure"
63
+ try:
64
+ connection.putrequest(
65
+ self.command, self.path, skip_host=True, skip_accept_encoding=True,
66
+ )
67
+ for key, value in headers.items():
68
+ connection.putheader(key, value)
69
+ connection.endheaders()
70
+ remaining = content_length
71
+ while remaining:
72
+ if time.monotonic() >= deadline:
73
+ raise TimeoutError('Proxy request deadline exceeded')
74
+ chunk = self.rfile.read(min(CHUNK_BYTES, remaining))
75
+ if not chunk:
76
+ raise ConnectionError('Incomplete request body')
77
+ connection.send(chunk)
78
+ remaining -= len(chunk)
79
+ return connection, connection.getresponse()
80
+ # lint: allow-PY001 rationale="retry once, then re-raise the expected upstream transport failure"
81
+ except (OSError, http.client.HTTPException):
82
+ connection.close()
83
+ if attempt + 1 == attempts:
84
+ raise
85
+ raise AssertionError('Upstream request attempts were not exhausted')
86
+
51
87
  def _proxy(self):
52
88
  # Preserve the browser's HTTP/1.1 connection across module requests.
53
89
  # Chunked response framing and the socket timeout bound each transfer.
@@ -78,24 +114,24 @@ def make_proxy_handler(*, backend_host: str, backend_port: int, forward_headers)
78
114
  headers = forward_headers(incoming_headers=self.headers.items(), client_ip=self.client_address[0])
79
115
  headers = {key: value for key, value in headers.items() if key.casefold() not in connection_tokens | HOP_HEADERS}
80
116
  headers['Connection'] = 'close'
81
- connection = http.client.HTTPConnection(backend_host, backend_port, timeout=IDLE_TIMEOUT_SECONDS)
82
117
  deadline = time.monotonic() + MAX_REQUEST_SECONDS
118
+ # lint: allow-PY001 rationale="translate the final external upstream transport failure into a sanitized gateway response"
119
+ try:
120
+ connection, response = self._request_upstream(
121
+ headers=headers, content_length=remaining, deadline=deadline,
122
+ )
123
+ # lint: allow-PY001 rationale="return a sanitized gateway error after retrying an expected upstream transport failure"
124
+ except (OSError, http.client.HTTPException) as error:
125
+ self.close_connection = True
126
+ LOGGER.error(
127
+ 'HTTPS proxy upstream transport failed method=%s backend=%s:%s error=%s',
128
+ self.command, backend_host, backend_port, type(error).__name__,
129
+ )
130
+ self.send_error(502, 'Upstream transport failed')
131
+ return
83
132
  response_started = False
84
133
  # lint: allow-PY001 rationale="close upstream on client disconnect or expected transport failure without disclosing exception details"
85
134
  try:
86
- connection.putrequest(self.command, self.path, skip_host=True, skip_accept_encoding=True)
87
- for key, value in headers.items():
88
- connection.putheader(key, value)
89
- connection.endheaders()
90
- while remaining:
91
- if time.monotonic() >= deadline:
92
- raise TimeoutError('Proxy request deadline exceeded')
93
- chunk = self.rfile.read(min(CHUNK_BYTES, remaining))
94
- if not chunk:
95
- raise ConnectionError('Incomplete request body')
96
- connection.send(chunk)
97
- remaining -= len(chunk)
98
- response = connection.getresponse()
99
135
  self.send_response(response.status, response.reason)
100
136
  response_tokens = {part.strip().casefold() for value in response.headers.get_all('Connection', []) for part in value.split(',')}
101
137
  for key, value in response.getheaders():
@@ -125,9 +161,13 @@ def make_proxy_handler(*, backend_host: str, backend_port: int, forward_headers)
125
161
  self.wfile.write(b'0\r\n\r\n')
126
162
  self.wfile.flush()
127
163
  # lint: allow-PY001 rationale="terminate a disconnected or failed external HTTP transport; emit only a sanitized upstream error"
128
- except (OSError, http.client.HTTPException):
164
+ except (OSError, http.client.HTTPException) as error:
129
165
  # A partial request/response cannot be reused for another request.
130
166
  self.close_connection = True
167
+ LOGGER.error(
168
+ 'HTTPS proxy response transport failed method=%s backend=%s:%s error=%s',
169
+ self.command, backend_host, backend_port, type(error).__name__,
170
+ )
131
171
  if not response_started:
132
172
  self.send_error(502, 'Upstream transport failed')
133
173
  finally:
@@ -124,15 +124,8 @@ class SafeSession:
124
124
  _configure_sql_logging(anchor)
125
125
  cls._memory_anchor = anchor
126
126
  print("\n" + "=" * 50)
127
- print(
128
- """
129
- 🧪 SWITCHING TO TEST MODE 🧪
130
- ┌──────────────────────────┐
131
- │ IN-MEMORY DATABASE │
132
- │ *All Data is Temporary │
133
- └──────────────────────────┘
134
- """
135
- )
127
+ print("TEST MODE: IN-MEMORY DATABASE")
128
+ print("All data is temporary")
136
129
  print("=" * 50 + "\n")
137
130
  return {"status": "ok", "message": "Using in-memory database"}
138
131
 
@@ -159,15 +152,7 @@ class SafeSession:
159
152
  _configure_sql_logging(conn)
160
153
  conn.close()
161
154
  print("\n" + "=" * 50)
162
- print(
163
- """
164
- 📝 RETURNING TO PRODUCTION MODE 📝
165
- ┌──────────────────────────┐
166
- │ PROD DATABASE │
167
- │ │
168
- └──────────────────────────┘
169
- """
170
- )
155
+ print("PRODUCTION MODE: FILE DATABASE")
171
156
  print("=" * 50 + "\n")
172
157
  return {"status": "ok", "message": "Using file database"}
173
158
 
@@ -9,6 +9,7 @@ import shutil
9
9
  import subprocess
10
10
  import sys
11
11
  import sysconfig
12
+ import threading
12
13
  from uuid import UUID, uuid4
13
14
 
14
15
  from app.data_directory import resolve_data_directory
@@ -28,6 +29,9 @@ class AppUpdateRejected(ValueError):
28
29
  """An update cannot be started in this installation or job state."""
29
30
 
30
31
 
32
+ _JOB_THREAD_LOCK = threading.Lock()
33
+
34
+
31
35
  def update_executable() -> Path:
32
36
  receipt = Path(sys.prefix) / 'uv-receipt.toml'
33
37
  if not receipt.is_file():
@@ -165,24 +169,23 @@ def _release_update_job_locked(job_id: str) -> None:
165
169
  def _job_lock():
166
170
  directory = jobs_directory()
167
171
  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:
172
+ # Threads share process-owned POSIX locks, so serialize them before taking
173
+ # the stable cross-process lock shared by all namespaces.
174
+ with _JOB_THREAD_LOCK:
175
+ with (directory / 'admission.lock').open('a+b') as handle:
181
176
  if os.name == 'nt':
182
177
  handle.seek(0)
183
- msvcrt.locking(handle.fileno(), msvcrt.LK_UNLCK, 1)
178
+ msvcrt.locking(handle.fileno(), msvcrt.LK_LOCK, 1)
184
179
  else:
185
- fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
180
+ fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
181
+ try:
182
+ yield
183
+ finally:
184
+ if os.name == 'nt':
185
+ handle.seek(0)
186
+ msvcrt.locking(handle.fileno(), msvcrt.LK_UNLCK, 1)
187
+ else:
188
+ fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
186
189
 
187
190
 
188
191
  def create_update_job(target_version: str) -> dict[str, object]:
@@ -76,17 +76,11 @@ def schedule_self_update(
76
76
  message=message,
77
77
  )
78
78
 
79
- uv_executable = shutil.which("uv")
80
- if uv_executable is None:
81
- raise RuntimeError("uv executable was not found on PATH; MetaList was not stopped")
82
-
83
79
  if platform_name == "win32":
84
80
  _resolve_windows_powershell()
85
81
  elif not Path("/bin/sh").is_file():
86
82
  raise RuntimeError("/bin/sh is required for MetaList self-update; MetaList was not stopped")
87
- install_command = _prepare_update(
88
- uv_executable=uv_executable, target_version=target_version, environ=environ,
89
- )
83
+ install_command = _prepare_update(target_version=target_version, environ=environ)
90
84
  updater_command: list[str]
91
85
  updater_options: dict[str, object]
92
86
  if platform_name == "win32":
@@ -0,0 +1,260 @@
1
+ """Provide MetaList's checksum-pinned updater runtime on every supported OS."""
2
+ from __future__ import annotations
3
+
4
+ from collections.abc import Mapping
5
+ import hashlib
6
+ import io
7
+ import os
8
+ from pathlib import Path
9
+ import platform
10
+ import re
11
+ import shutil
12
+ import subprocess
13
+ import sys
14
+ import tarfile
15
+ from tempfile import NamedTemporaryFile
16
+ from urllib.request import urlopen
17
+ import zipfile
18
+
19
+
20
+ # Official immutable uv 0.12.17 release archives. These values match the
21
+ # upstream sha256.sum asset for the same release.
22
+ INSTALLER_VERSION = "0.12.17"
23
+ INSTALLER_ARCHIVE_SHA256 = {
24
+ "aarch64-apple-darwin": "85f00cbdc6dd3e97eba4c31b4d014375a9fdfe8f570023b84e5102fc3456896b",
25
+ "aarch64-pc-windows-msvc": "3e1aa6849d77f0e00dc865e4afab5c5b32de053e21fe35bf5ad5cec3734ec976",
26
+ "aarch64-unknown-linux-gnu": "d636d1b678e9e7f367ecb22b46bd1cabbed234d6bc3b4d96365d2b507f72f86c",
27
+ "aarch64-unknown-linux-musl": "a6096da273d548cb9f277d237a01ac7344a39ef0f455c0e148e4dc9737c1596b",
28
+ "armv7-unknown-linux-gnueabihf": "796e101976e18a0f92d90b6f3efd289f77fbc5afd1aaa6fdedd3c84c209eabab",
29
+ "armv7-unknown-linux-musleabihf": "421c5f9c53d49088e705e7a64fea147082a2191178ea1d8c51d9ad758ca899f6",
30
+ "i686-pc-windows-msvc": "1d3653cb6eafd1e675e979d7cd8eeac1456202f8927a2216419993077a4c473f",
31
+ "i686-unknown-linux-gnu": "3faa79a70e2a1aa6eb3cd78eba02263f3e4b17b6be584a01429968f92ff74966",
32
+ "i686-unknown-linux-musl": "fc05220cfa1c6585e0e209877c228393b25a96e17cd43028b06566ac894cd078",
33
+ "powerpc64le-unknown-linux-gnu": "eb6dbedaaa622af32cba2388e0d2d47e34c8724d4214a129d614b96f071d8e53",
34
+ "riscv64gc-unknown-linux-gnu": "65b807b24357133c49d97ca9394ee7e13ca62822e1095376c809654b35f39cf2",
35
+ "riscv64gc-unknown-linux-musl": "87c339a735b578f285b961bebdefe0a671f3652477d1e84dc69cd1a829f45e92",
36
+ "s390x-unknown-linux-gnu": "d99f69f47fc5e0975d6e7a5745cf2332317015caffb3bb81e5154cc7b66fa77a",
37
+ "x86_64-apple-darwin": "8dcf05a8c809bb3c471d2b614788ba27a6e41298fc8c31ac84b5f4339fd468e5",
38
+ "x86_64-pc-windows-msvc": "a252121d5b59398fcb137c6ea448176459a44010f33f67e0072305a637119ca7",
39
+ "x86_64-unknown-linux-gnu": "fa82fd8dde8e8eefdecada6aa0889666556cfceb690d06e0c3bca49eb3070a63",
40
+ "x86_64-unknown-linux-musl": "6401c4665d8fa2a9893e087c91f585430738e3170f5398a1141483efb4a93310",
41
+ }
42
+
43
+
44
+ def _installer_version(executable: str, environ: Mapping[str, str]) -> tuple[int, int, int]:
45
+ completed = subprocess.run(
46
+ [executable, "--version"], env=dict(environ), capture_output=True,
47
+ text=True, check=True, timeout=30,
48
+ )
49
+ match = re.fullmatch(
50
+ r"uv (\d+)\.(\d+)\.(\d+)(?: \([^\r\n]+\))?",
51
+ completed.stdout.strip(),
52
+ )
53
+ if match is None:
54
+ raise RuntimeError("Cannot verify the uv installer version; MetaList was not stopped")
55
+ return tuple(int(part) for part in match.groups())
56
+
57
+
58
+ def _normalized_architecture() -> str:
59
+ machine = platform.machine().casefold()
60
+ aliases = {
61
+ "amd64": "x86_64", "x86_64": "x86_64",
62
+ "arm64": "aarch64", "aarch64": "aarch64",
63
+ "x86": "i686", "i386": "i686", "i686": "i686",
64
+ "armv7": "armv7", "armv7l": "armv7",
65
+ "ppc64le": "powerpc64le", "powerpc64le": "powerpc64le",
66
+ "riscv64": "riscv64gc", "riscv64gc": "riscv64gc",
67
+ "s390x": "s390x",
68
+ }
69
+ if machine not in aliases:
70
+ raise RuntimeError(f"Unsupported MetaList updater platform architecture: {machine}")
71
+ return aliases[machine]
72
+
73
+
74
+ def _linux_environment() -> str:
75
+ libc_name = platform.libc_ver()[0].casefold()
76
+ if libc_name in {"glibc", "gnu libc"}:
77
+ return "gnu"
78
+ if libc_name == "musl":
79
+ return "musl"
80
+ raise RuntimeError(f"Unsupported MetaList updater platform C library: {libc_name or 'unknown'}")
81
+
82
+
83
+ def _installer_target(platform_name: str) -> str:
84
+ architecture = _normalized_architecture()
85
+ if platform_name == "win32":
86
+ target = f"{architecture}-pc-windows-msvc"
87
+ elif platform_name == "darwin":
88
+ target = f"{architecture}-apple-darwin"
89
+ elif platform_name == "linux":
90
+ environment = _linux_environment()
91
+ if architecture == "armv7":
92
+ if environment == "gnu":
93
+ abi = "gnueabihf"
94
+ elif environment == "musl":
95
+ abi = "musleabihf"
96
+ else:
97
+ raise AssertionError(f"Unexpected Linux environment: {environment}")
98
+ else:
99
+ abi = environment
100
+ target = f"{architecture}-unknown-linux-{abi}"
101
+ else:
102
+ raise RuntimeError(f"Unsupported MetaList updater platform: {platform_name}")
103
+ if target not in INSTALLER_ARCHIVE_SHA256:
104
+ raise RuntimeError(f"Unsupported MetaList updater platform target: {target}")
105
+ return target
106
+
107
+
108
+ def _required_absolute_directory(environ: Mapping[str, str], name: str) -> Path:
109
+ if name not in environ:
110
+ raise RuntimeError(f"{name} is required for the MetaList update installer")
111
+ directory = Path(environ[name])
112
+ if not directory.is_absolute():
113
+ raise RuntimeError(f"{name} must be an absolute path for the MetaList update installer")
114
+ return directory
115
+
116
+
117
+ def _installer_cache_root(*, platform_name: str, environ: Mapping[str, str]) -> Path:
118
+ if platform_name == "win32":
119
+ return _required_absolute_directory(environ, "LOCALAPPDATA") / "MetaList" / "update-tools"
120
+ if platform_name == "darwin":
121
+ home = _required_absolute_directory(environ, "HOME")
122
+ return home / "Library" / "Caches" / "MetaList" / "update-tools"
123
+ if platform_name == "linux":
124
+ if "XDG_CACHE_HOME" in environ:
125
+ cache_home = _required_absolute_directory(environ, "XDG_CACHE_HOME")
126
+ else:
127
+ home = _required_absolute_directory(environ, "HOME")
128
+ cache_home = home / ".cache"
129
+ return cache_home / "metalist" / "update-tools"
130
+ raise RuntimeError(f"Unsupported MetaList updater platform: {platform_name}")
131
+
132
+
133
+ def _archive_name(*, target: str, platform_name: str) -> str:
134
+ if platform_name == "win32":
135
+ extension = "zip"
136
+ elif platform_name in {"darwin", "linux"}:
137
+ extension = "tar.gz"
138
+ else:
139
+ raise RuntimeError(f"Unsupported MetaList updater platform: {platform_name}")
140
+ return f"uv-{target}.{extension}"
141
+
142
+
143
+ def _download_installer_archive(asset_name: str) -> bytes:
144
+ url = f"https://github.com/astral-sh/uv/releases/download/{INSTALLER_VERSION}/{asset_name}"
145
+ with urlopen(url, timeout=120) as response:
146
+ return response.read()
147
+
148
+
149
+ def _extract_installer_binary(
150
+ *, archive_body: bytes, asset_name: str, platform_name: str, target: str,
151
+ ) -> bytes:
152
+ if platform_name == "win32":
153
+ member_name = "uv.exe"
154
+ elif platform_name in {"darwin", "linux"}:
155
+ member_name = f"uv-{target}/uv"
156
+ else:
157
+ raise RuntimeError(f"Unsupported MetaList updater platform: {platform_name}")
158
+ if asset_name.endswith(".zip"):
159
+ with zipfile.ZipFile(io.BytesIO(archive_body)) as archive:
160
+ return archive.read(member_name)
161
+ with tarfile.open(fileobj=io.BytesIO(archive_body), mode="r:gz") as archive:
162
+ member = archive.extractfile(member_name)
163
+ if member is None:
164
+ raise RuntimeError("Verified update installer archive is missing the uv executable")
165
+ return member.read()
166
+
167
+
168
+ def _publish_bytes(*, destination: Path, body: bytes, mode: int) -> None:
169
+ destination.parent.mkdir(parents=True, exist_ok=True)
170
+ with NamedTemporaryFile(
171
+ dir=destination.parent, prefix=destination.name + "-", suffix=".tmp", delete=False,
172
+ ) as staged:
173
+ staged.write(body)
174
+ staged.flush()
175
+ os.fsync(staged.fileno())
176
+ staged_path = Path(staged.name)
177
+ os.chmod(staged_path, mode)
178
+ os.replace(staged_path, destination)
179
+
180
+
181
+ def _load_verified_archive(
182
+ *, archive_path: Path, expected_digest: str, asset_name: str,
183
+ ) -> bytes:
184
+ if archive_path.exists():
185
+ archive_body = archive_path.read_bytes()
186
+ if hashlib.sha256(archive_body).hexdigest() != expected_digest:
187
+ raise RuntimeError(
188
+ "Cached update installer archive failed checksum verification; "
189
+ "MetaList was not stopped"
190
+ )
191
+ return archive_body
192
+ print(f"Preparing verified MetaList update installer uv {INSTALLER_VERSION}...", flush=True)
193
+ archive_body = _download_installer_archive(asset_name)
194
+ if hashlib.sha256(archive_body).hexdigest() != expected_digest:
195
+ raise RuntimeError(
196
+ "Downloaded update installer failed checksum verification; MetaList was not stopped"
197
+ )
198
+ _publish_bytes(destination=archive_path, body=archive_body, mode=0o600)
199
+ return archive_body
200
+
201
+
202
+ def _managed_installer(*, platform_name: str, environ: Mapping[str, str]) -> str:
203
+ target = _installer_target(platform_name)
204
+ asset_name = _archive_name(target=target, platform_name=platform_name)
205
+ directory = _installer_cache_root(platform_name=platform_name, environ=environ) / (
206
+ f"uv-{INSTALLER_VERSION}-{target}"
207
+ )
208
+ archive_path = directory / asset_name
209
+ archive_body = _load_verified_archive(
210
+ archive_path=archive_path,
211
+ expected_digest=INSTALLER_ARCHIVE_SHA256[target],
212
+ asset_name=asset_name,
213
+ )
214
+ expected_binary = _extract_installer_binary(
215
+ archive_body=archive_body, asset_name=asset_name,
216
+ platform_name=platform_name, target=target,
217
+ )
218
+ if platform_name == "win32":
219
+ executable = directory / "uv.exe"
220
+ else:
221
+ assert platform_name in {"darwin", "linux"}
222
+ executable = directory / "uv"
223
+ if executable.exists():
224
+ if executable.read_bytes() != expected_binary:
225
+ raise RuntimeError(
226
+ "Cached update installer failed checksum verification; MetaList was not stopped"
227
+ )
228
+ else:
229
+ _publish_bytes(destination=executable, body=expected_binary, mode=0o700)
230
+ if platform_name != "win32":
231
+ os.chmod(executable, 0o700)
232
+ return str(executable)
233
+
234
+
235
+ def resolve_update_installer(*, platform_name: str, environ: Mapping[str, str]) -> str:
236
+ executable = _managed_installer(platform_name=platform_name, environ=environ)
237
+ expected_version = tuple(int(part) for part in INSTALLER_VERSION.split("."))
238
+ if _installer_version(executable, environ) != expected_version:
239
+ raise RuntimeError(
240
+ "Verified update installer reported the wrong version; MetaList was not stopped"
241
+ )
242
+ return executable
243
+
244
+
245
+ def repair_windows_update() -> None:
246
+ if sys.platform != "win32":
247
+ raise RuntimeError("This recovery launcher is for Windows installations")
248
+ metalist_executable = shutil.which("metalist")
249
+ if metalist_executable is None:
250
+ raise RuntimeError("The existing MetaList command must be available on PATH")
251
+ environ = dict(os.environ)
252
+ installer = resolve_update_installer(platform_name="win32", environ=environ)
253
+ # The already-installed updater discovers uv through PATH. Preserve that
254
+ # updater's preflight, shutdown, backup, offline install, and restart flow.
255
+ environ["PATH"] = str(Path(installer).parent) + os.pathsep + environ["PATH"]
256
+ subprocess.run([metalist_executable, "update"], env=environ, check=True)
257
+
258
+
259
+ if __name__ == "__main__":
260
+ repair_windows_update()
@@ -16,6 +16,7 @@ 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
18
  from app.services.windows_process_control import stop_process_tree as stop_windows_process_tree
19
+ from app.services.update_installer import resolve_update_installer
19
20
 
20
21
 
21
22
  def _run_checked(command: list[str], *, environ: Mapping[str, str], directory: Path) -> str:
@@ -86,12 +87,12 @@ def _probe_candidate(*, python: str, executable: str, directory: Path,
86
87
  _stop_failed_namespace_launch(process=process)
87
88
 
88
89
 
89
- def prepare_update(*, uv_executable: str, target_version: str,
90
- environ: Mapping[str, str]) -> list[str]:
90
+ def prepare_update(*, target_version: str, environ: Mapping[str, str]) -> list[str]:
91
91
  # A tool environment is replaced during install: pin its external base interpreter.
92
92
  python = str(Path(sys._base_executable).resolve())
93
93
  if not Path(python).is_file():
94
94
  raise RuntimeError("The current base Python interpreter is missing; MetaList was not stopped")
95
+ uv_executable = resolve_update_installer(platform_name=sys.platform, environ=environ)
95
96
  print(f"Checking MetaList v{target_version} installation and startup with Python {sys.version.split()[0]}...", flush=True)
96
97
  with TemporaryDirectory(prefix="metalist-update-check-") as temporary:
97
98
  directory = Path(temporary)
@@ -165,7 +165,7 @@ def discover_typescript_source_paths(project_root: Path) -> list[Path]:
165
165
 
166
166
 
167
167
  def _path_rel(project_root: Path, path: Path) -> str:
168
- return os.path.relpath(path, project_root)
168
+ return path.relative_to(project_root).as_posix()
169
169
 
170
170
 
171
171
  def _codeframe(lines: list[str], lineno: int, col: int, context_lines: int) -> str:
@@ -72,7 +72,7 @@ class _RouteDecorator:
72
72
 
73
73
 
74
74
  def _path_rel(project_root: Path, path: Path) -> str:
75
- return os.path.relpath(path, project_root)
75
+ return path.relative_to(project_root).as_posix()
76
76
 
77
77
 
78
78
  def _suppressed(lines: list[str], lineno: int, rule_id: str) -> bool:
@@ -1,3 +1,3 @@
1
1
  """Single authoritative MetaList application version."""
2
2
 
3
- __version__ = "0.7.2"
3
+ __version__ = "0.7.4"