MetaList 0.6.1__tar.gz → 0.6.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {metalist-0.6.1 → metalist-0.6.3}/PKG-INFO +30 -8
- {metalist-0.6.1 → metalist-0.6.3}/README.md +28 -6
- {metalist-0.6.1 → metalist-0.6.3}/app/api/middleware/auth.py +2 -0
- metalist-0.6.3/app/api/routes/app_updates.py +55 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/exception_boundaries.py +12 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/https_proxy.py +9 -3
- {metalist-0.6.1 → metalist-0.6.3}/app/main.py +2 -0
- metalist-0.6.3/app/services/app_update_worker.py +42 -0
- metalist-0.6.3/app/services/app_updates.py +190 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/client_state_service.py +5 -1
- {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_switcher.py +55 -2
- {metalist-0.6.1 → metalist-0.6.3}/app/services/self_update.py +21 -18
- {metalist-0.6.1 → metalist-0.6.3}/app/services/shell_session_service.py +1 -1
- metalist-0.6.3/app/services/update_preflight.py +131 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/windows_process_control.py +37 -4
- {metalist-0.6.1 → metalist-0.6.3}/app/startup_sanity_config.py +3 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/config/command_palette_tags.json +1 -1
- {metalist-0.6.1 → metalist-0.6.3}/app/static/css/main.css +16 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/main.js +3 -0
- metalist-0.6.3/app/static/js/modules/app-update-service.js +106 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/auth.js +1 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/command-palette-controller.js +5 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/config.js +1 -0
- metalist-0.6.3/app/static/js/modules/modals/version-info-modal.js +313 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/version.py +1 -1
- {metalist-0.6.1 → metalist-0.6.3}/convert-from-legacy.py +4 -1
- {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/PKG-INFO +30 -8
- {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/SOURCES.txt +5 -0
- {metalist-0.6.1 → metalist-0.6.3}/pyproject.toml +1 -1
- metalist-0.6.1/app/static/js/modules/modals/version-info-modal.js +0 -190
- {metalist-0.6.1 → metalist-0.6.3}/MANIFEST.in +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/__init__.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/__init__.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/deps.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/dev.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/note_requests.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/request_auth.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/ai.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/auth.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/backups.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/files.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/notes.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/ontology.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/reminders.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/remote_images.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/test.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/transactions.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/api/upload_limits.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/config.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/data_directory.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/__init__.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/backup_settings_sql.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/engine.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/file_schema.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/file_session.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/files_sql.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/link_titles_sql.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/live_recovery.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/migrations.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/notes_sql.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/ontology_rules_sql.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/reminders_sql.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/retire_sounds.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/schema.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/search_history_sql.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/session.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/settings_sql.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/tab_state_sql.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/db/version.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/encryption_audit.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/models/commands.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/models/database.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/models/entities.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/models/enums.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/models/linked_list.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/models/list_operations.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/models/list_traversal.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/models/note_crud.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/models/utils.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/presentation/__init__.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/presentation/render/__init__.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/presentation/render/note_renderer.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/presentation/templates.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/security/authenticated_logging.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/security/encryption.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/security/http_headers.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/security/note_html.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/security/request_boundary.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/security/sensitive_cache.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/security/sensitive_logging.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/security/shell_execution.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/security/validation_errors.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/server_runtime.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/__init__.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/__init__.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/actions.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/cloud_privacy.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/context.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/evidence_serialization.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/inference.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/investigation.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/model_policy.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/ollama_inference.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/openai_cost_tracking.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/openai_inference.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/permissions.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/prompt_settings.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/prompts/__init__.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/prompts/final-response.md +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/prompts/system.md +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/prompts/tool-result.md +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/retrieval_settings.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/runtime.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/scope.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/skill_settings.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/skills/__init__.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/skills/scoped-investigation.md +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/tagging.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/tagging_run.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/token_estimation.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/tools.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/trace.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/ai_chat.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/ai_chat_rendering.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/ai_chat_stream.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/auth.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/auth_cache_state.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/auth_service.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/backlink_index.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/backlinks.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/backup_service.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/backup_settings_service.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/bulk_operation.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/content_cache.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/content_formatting.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/diagnostics.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/embedded_references.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/encryption.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/exception_capture.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/file_registry.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/file_storage.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/footnote_layout.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/footnote_rendering.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/hierarchy.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/html_export.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/html_unformatting.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/hydration_state.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/image_size_formatting.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/inline_image_occurrences.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/input_errors.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/integrity.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/latex_rendering.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/link_titles.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/login_rate_limit.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/maintenance_mode.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/managed_ollama_runtime.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/markdown_rendering.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_deletion_jobs.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_deletion_worker.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_rename_jobs.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_rename_worker.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_runtime_guard.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/note_fullscreen.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/note_image_tags.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/note_ordering.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/note_store.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/ollama_provider.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/ontology_rules_store.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/openai_credentials.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/password_note_fields.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/reference_presentation.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/reminders.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/remote_image_proxy.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/request_recovery.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/resource_limits.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/root_sorting.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/runtime_generation.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/runtime_hardening.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/runtime_lock.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/search_history.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/search_history_storage.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/search_index.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/search_query.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/search_text.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/selected_text_tag.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/session_timeout_service.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/snapshot.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/store.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/structured_note_renderers.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/sync.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/sync_state.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/tab_state.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/tag_case.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/tag_ontology.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/tag_rename.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/tag_suggestions.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/tag_term_matching.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/test_reset.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/tokens.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/undo_state.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/update_backups.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/view_cache.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/view_diff.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/services/view_state.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/startup_environment.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/startup_js_sanity.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/startup_sanity.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/css/floating-notes.css +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/css/forms.css +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/img/favicon.ico +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/locked.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/activity-tracker.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/agent-prompt-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/agent-retrieval-settings.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-agent-debug-view.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-chat-api.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-chat-panel-controller.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-chat-panel-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-thinking-level-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/bulk-elapsed-clock.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/bulk-proposal-ui.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/cloud-privacy-policy.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/proposal-menu.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/api-client.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/api-failure-classification-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/application-state.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/async-result.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/client-state-api.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/client-state-migration.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/activation-auth-policy.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/backup-command-availability.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/click-activation-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/endpoint-registry.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/note-layout-preferences.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/preferences-store.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/tag-config-loader.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/usage-store.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/connectivity-monitor.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/context-menu/context-menu-registry.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/context-menu/context-menu-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/context-menu/context-menu-target-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/context-menu/input-caret-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/disconnect-editing-state-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/dom-utils-fixed.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/dom-utils.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/editor-commands.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/editor-selection.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/editor-toolbar.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/elapsed-time.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/error-handler.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/error-overlay.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/expected-errors.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/location-flags.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/login-namespace-picker.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/maintenance.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/agent-prompt-editor-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/ai-agent-settings-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/alphabetize-root-notes-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/backup-restore-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/backup-result-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/backup-retention-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/backup-settings-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/base-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/confirmation-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/delete-namespace-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/delete-namespace-validation.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/help-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/namespace-loading-page.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/namespace-modals.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/namespace-ports-profile.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/namespace-rename-validation.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/note-layout-appearance-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/ontology-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/password-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/prioritize-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/random-password-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/reminder-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/reset-updated-at-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/search-suggestion-statistics-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/session-timeout-modal.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/content-actions.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/history-actions.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/note-actions.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/search-actions.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/selection-actions.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/ui-actions.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/context-menu-events.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/focus-events.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/inactivity-events.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/input-events.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/keyboard-events.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/mouse-events.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/search-events.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/mode-context.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/mode-logger.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/mode-manager-controller.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/add-style-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/animated-scroll-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/backlinks-navigation-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/backlinks-panel-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/clipboard-shortcut-policy-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/collapse-affordance-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/collapse-editing-policy-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/command-gate-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/context-menu-target-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/deselect-selection-state-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/differential-view-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/edit-session-collapse-policy-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/edit-session-collapse-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/embedded-image-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/file-drop-policy-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/file-image-preview-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/file-reference-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/filtered-refresh-selection-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/floating-note-dom.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/floating-note-layout.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/floating-note-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/footnote-navigation-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/history-selection-policy-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/history-shortcut-policy-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/html-paste-sanitizer-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/image-context-menu-action-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/image-file-insert-choice-modal-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/infinite-scroll-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/link-context-menu-action-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/link-title-prefetch-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/markdown-render-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/mermaid-render-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-click-target-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-clipboard-write-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-collapse-animation-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-context-menu-action-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-delete-shortcut-policy-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-drag-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-fullscreen-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-reposition-animation-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-split-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-timestamp-hover-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/password-clipboard-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/polling-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/reference-source-navigation-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/remote-image-proxy-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/remove-formatting-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/root-date-separator-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/root-sort-indicator-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/root-sort-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/scroll-anchor-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/scroll-restoration-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/scroll-to-top-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-contexts-overlay-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-debounce-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-input-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-interaction-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-redaction-reveal-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestion-statistics-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestion-window-labels-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestion-windows-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestions-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-syntax-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/selected-formatting-removal-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/selected-text-tag-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/shell-output-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-dom-cache-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-drag-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-duplication-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-state-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-bar-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-input-selection-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-proposal-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-suggestions-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-syntax-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/todo-toggle-editing-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/untagged-view-indicator-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/view-mode-search-shortcut-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/note-html-sanitizer.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/password-generator.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/password-operation-response.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/password-policy.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/password-strength.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/reminder-store.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/reminder-surface-service.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/selection-markers.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/session-auth.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/tag-token.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/uuid.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/namespace-deleted.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/namespace-renamed.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/dompurify-3.4.13.LICENSE.txt +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/dompurify-3.4.13.min.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/markdown-it-14.3.2.LICENSE.txt +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/markdown-it-14.3.2.min.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/mermaid-11.16.1.LICENSE.txt +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/mermaid-11.16.1.min.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/zxcvbn-4.4.2.LICENSE.txt +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/zxcvbn-4.4.2.js +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/note-html-policy.json +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/static/video/login-startup.mp4 +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/templates/base.html +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/templates/index.html +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/templates/locked.html +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/templates/maintenance.html +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/templates/namespace_deleted.html +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/templates/namespace_renamed.html +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/templates/notes_list.html +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/upload_limits.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/__init__.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/add_selected_text_tag.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/alphabetize_root_notes.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/base.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/bulk_tag_proposals.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/collapse.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/copy_note.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/create_child.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/create_note.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/create_sibling.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/delete_subtree.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/delete_tag.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/expand.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/indent.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/lock.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/move.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/move_to_top.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/outdent.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/paste_child.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/paste_sibling.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/prefetch_link_titles.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/prioritize.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/redo.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/rename_tag.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/reset_updated_at.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/resize_image.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/run_shell.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/search_comment_autofill.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/search_context_tags.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/set_collapse_bulk.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/set_collapse_in_context.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/split_note.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/tag_proposals.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/toggle_reference_mode.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/toggle_todo_done.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/undo.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/unformat_content.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/update_content.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/usecases/view.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/utils/__init__.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/utils/encryption.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/app/utils/text_utils.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/main.py +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/dependency_links.txt +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/entry_points.txt +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/requires.txt +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/top_level.txt +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/scripts/generate-lan-cert.sh +0 -0
- {metalist-0.6.1 → metalist-0.6.3}/setup.cfg +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metalist
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: A minimalist single-user note-taking app with SSR, fast in-memory tree operations, and efficient sync.
|
|
5
5
|
Project-URL: Homepage, https://github.com/evolvingstuff/metalist
|
|
6
6
|
Project-URL: Repository, https://github.com/evolvingstuff/metalist
|
|
7
7
|
Project-URL: Issues, https://github.com/evolvingstuff/metalist/issues
|
|
8
|
-
Requires-Python: <3.
|
|
8
|
+
Requires-Python: <3.15,>=3.10
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
Requires-Dist: argon2-cffi==25.1.0
|
|
11
11
|
Requires-Dist: cryptography==50.0.0
|
|
@@ -48,6 +48,20 @@ A minimalist single-user note-taking app focused on server-side rendering (SSR),
|
|
|
48
48
|
- Multi-tab search contexts with server-persisted scroll/search state (survives browser restarts)
|
|
49
49
|
- Manual namespace backups/restores to a user-selected backup folder with retention controls
|
|
50
50
|
|
|
51
|
+
## Changes in 0.6.3
|
|
52
|
+
|
|
53
|
+
- Restored HTTPS connection reuse and aligned the pending accept queue with the existing worker capacity, so a browser’s six-connection startup burst is admitted.
|
|
54
|
+
- Every page load or refresh checks PyPI asynchronously. A newly discovered release shows a dismissible notice linking to Version Info; typing `update` in the menu also finds it.
|
|
55
|
+
- Managed uv installations can update from Version Info, using the existing preflight, verified immutable backups, and namespace restart flow. Source checkouts and unmanaged installations explain why in-app installation is unavailable.
|
|
56
|
+
- Release validation now transfers every installed startup asset through concurrent persistent HTTP/HTTPS connections on all supported platforms and Python versions. Actual Edge startup over LAN-style HTTPS is also required on Windows before publication.
|
|
57
|
+
|
|
58
|
+
## Changes in 0.6.2
|
|
59
|
+
|
|
60
|
+
- Namespace startup allows two minutes, with progress every five seconds, instead of failing after 12 seconds on a busy machine. `METALIST_STARTUP_TIMEOUT_SECONDS` can increase the allowance. Failed children are stopped and reaped.
|
|
61
|
+
- Updates preserve the running Python interpreter and validate a disposable installation and namespace startup before stopping live servers. uv then installs the tested package/dependency versions from its cache without network access.
|
|
62
|
+
- Python 3.14 is supported. Command-line legacy imports also work when Python lacks Tk's native GUI component.
|
|
63
|
+
- Release validation now includes the real uv update, backups, and multi-namespace restart on Windows, macOS, and Linux across Python 3.10–3.14.
|
|
64
|
+
|
|
51
65
|
## Changes in 0.6.1
|
|
52
66
|
|
|
53
67
|
- Removed the unused performance overlay, including its menu option and background state updates, fixing a fatal redundant-state error during note rearranging. Obsolete overlay preferences and command usage are discarded automatically.
|
|
@@ -115,15 +129,19 @@ uv tool install metalist
|
|
|
115
129
|
metalist
|
|
116
130
|
```
|
|
117
131
|
|
|
118
|
-
|
|
132
|
+
For a persistent uv installation, you can update from inside MetaList: type **update** in the menu, open **Version Info**, and choose **Update to …** when a newer release is available. Each browser load or refresh asks the server to check PyPI asynchronously. A reminder-style notice links to Version Info the first time a newer version is discovered; the announced version is remembered in that namespace's client preferences. A slow or failed check does not block the app. Version Info can retry a failed check.
|
|
133
|
+
|
|
134
|
+
The in-app updater checks the selected release, creates verified backups, and restarts every namespace. Keep Version Info open to follow progress and reload when ready; closing and reopening it in the same page resumes progress. Signing in again may be necessary. Source checkouts and unmanaged installations still check for releases, but show why in-app installation is unavailable. Update job records and logs live under `~/MetaList/update-jobs/` (or the configured data directory). If installation/restart fails after servers stop, inspect the updater output and run `metalist` on the server to restart it; there is no automatic rollback.
|
|
135
|
+
|
|
136
|
+
You can also update and restart MetaList with one cross-platform command:
|
|
119
137
|
```bash
|
|
120
138
|
metalist update
|
|
121
139
|
```
|
|
122
|
-
The updater checks the installed version against the latest PyPI release first. If MetaList is already current, it reports the installed version and leaves all running namespaces untouched. When an update is available, it checks the
|
|
140
|
+
The updater checks the installed version against the latest PyPI release first. If MetaList is already current, it reports the installed version and leaves all running namespaces untouched. When an update is available, it checks installer prerequisites and uses uv to install the candidate into a disposable tool environment with the current base Python interpreter. It checks dependencies and starts a temporary namespace to verify HTTP readiness, version, and runtime assets. A failed preflight leaves the current installation and running namespaces unchanged. After preflight succeeds, it stops running namespaces, and creates and verifies a new backup of every namespace before installation can begin. Each archive includes the complete notes/settings database, the attachments database when present, and any legacy search-history database. Locked namespaces are backed up with their encrypted data and key metadata intact, without requiring a password.
|
|
123
141
|
|
|
124
142
|
Backups are saved to `~/MetaList/namespaces/<namespace>/backups/<namespace>-<timestamp>.metalist-backup.tar.gz`; the updater prints each verified path. Existing backups remain unchanged and are not pruned. If any backup fails, the update aborts with the current installation intact; run `metalist` to restart the stopped servers after resolving the failure.
|
|
125
143
|
|
|
126
|
-
Only after all backups pass does the updater hand off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock. It
|
|
144
|
+
Only after all backups pass does the updater hand off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock. It delegates installation to uv, pinning the current base interpreter and the exact tested package/dependency versions, using the cache populated during preflight with network access disabled. It then launches MetaList again, and reports the installed version (for example, `MetaList updated to v0.5.0.`). This protection requires an installed version containing the backup safeguard and applies to `metalist update` and the in-app update action; direct pip/uv install commands do not run it.
|
|
127
145
|
|
|
128
146
|
For pip, users can run `pip install metalist`. For a non-editable local install from this checkout, use `uv pip install .` or `pip install .` instead of the editable command below.
|
|
129
147
|
|
|
@@ -283,6 +301,10 @@ convert-from-legacy.py --namespace work --input /path/to/legacy-export.json
|
|
|
283
301
|
|
|
284
302
|
If `--namespace`, `--port`, or `--https-port` are omitted, the import script prompts for them and saves the resulting launch profile inside the target namespace DB. That means a one-time import into `work` can immediately seed later shorthand launches like `metalist work`.
|
|
285
303
|
|
|
304
|
+
Namespace startup allows 120 seconds per child, reports progress every five seconds, and stops/reaps a failed child instead of leaving it running after reporting failure. Set `METALIST_STARTUP_TIMEOUT_SECONDS` to a positive number of seconds to allow more time on a heavily loaded machine. A crashed child still fails immediately. This is a startup allowance, not a guarantee that any machine will finish within two minutes.
|
|
305
|
+
|
|
306
|
+
The preflight is a disposable empty namespace; it cannot guarantee startup against every real database or prevent a later disk/process failure. If installation succeeds but real namespace startup fails, the new package remains installed: resolve the reported startup issue and run `metalist` again. No automatic code/database rollback is attempted, and backups remain unchanged.
|
|
307
|
+
|
|
286
308
|
### Publishing
|
|
287
309
|
For the real user-facing install flow:
|
|
288
310
|
```bash
|
|
@@ -292,15 +314,15 @@ uv tool install metalist
|
|
|
292
314
|
metalist
|
|
293
315
|
```
|
|
294
316
|
|
|
295
|
-
This repo now packages itself under the PyPI distribution name `metalist`. Current releases support Python 3.10 through 3.
|
|
317
|
+
This repo now packages itself under the PyPI distribution name `metalist`. Current releases support Python 3.10 through 3.14.
|
|
296
318
|
|
|
297
319
|
Recommended release path:
|
|
298
320
|
1. In the existing PyPI project `metalist`, configure GitHub Trusted Publishing for `evolvingstuff/metalist` and the workflow file `.github/workflows/publish-pypi.yml`.
|
|
299
|
-
2. Push the candidate commit to a branch and wait for the `Publish to PyPI` validation workflow to pass for that exact commit. GitHub-hosted Windows, macOS, and Linux runners install the built wheel and test application startup outside the source checkout on Python 3.10 through 3.
|
|
321
|
+
2. Push the candidate commit to a branch and wait for the `Publish to PyPI` validation workflow to pass for that exact commit. GitHub-hosted Windows, macOS, and Linux runners install the built wheel and test application startup outside the source checkout on Python 3.10 through 3.14. Every matrix job also exercises the real `metalist update` path with uv, backups, interpreter preservation, and two-namespace restart against a local release index. The old-version fixture carries the candidate updater so this validates the mechanism being shipped; it does not retroactively fix older installed updaters. You do not need those operating systems locally. Archive verification also checks runtime files, including agent Markdown resources, against both the wheel and source distribution.
|
|
300
322
|
3. Only after every required check passes, create and push the release tag for that same commit. Any intervening change requires a new validation run before tagging. Branch and pull-request runs validate without publishing.
|
|
301
323
|
4. The tag workflow repeats validation and publishes the same artifacts tested by its full matrix. Manual publication also requires all checks to pass. Users can then run it with `uvx metalist`, install it persistently with `uv tool install metalist`, or install it with `pip install metalist`.
|
|
302
324
|
|
|
303
|
-
If `--input` is omitted, a file picker opens (when `tkinter`
|
|
325
|
+
If `--input` is omitted, a file picker opens (when `tkinter` and its native `_tkinter` component are available).
|
|
304
326
|
Notes tagged with `@implies` are converted into ontology rules and are not imported as notes. Legacy rules that are invalid under the current ontology grammar are reported and skipped while valid rules continue importing.
|
|
305
327
|
|
|
306
328
|
### Run Tests
|
|
@@ -12,6 +12,20 @@ A minimalist single-user note-taking app focused on server-side rendering (SSR),
|
|
|
12
12
|
- Multi-tab search contexts with server-persisted scroll/search state (survives browser restarts)
|
|
13
13
|
- Manual namespace backups/restores to a user-selected backup folder with retention controls
|
|
14
14
|
|
|
15
|
+
## Changes in 0.6.3
|
|
16
|
+
|
|
17
|
+
- Restored HTTPS connection reuse and aligned the pending accept queue with the existing worker capacity, so a browser’s six-connection startup burst is admitted.
|
|
18
|
+
- Every page load or refresh checks PyPI asynchronously. A newly discovered release shows a dismissible notice linking to Version Info; typing `update` in the menu also finds it.
|
|
19
|
+
- Managed uv installations can update from Version Info, using the existing preflight, verified immutable backups, and namespace restart flow. Source checkouts and unmanaged installations explain why in-app installation is unavailable.
|
|
20
|
+
- Release validation now transfers every installed startup asset through concurrent persistent HTTP/HTTPS connections on all supported platforms and Python versions. Actual Edge startup over LAN-style HTTPS is also required on Windows before publication.
|
|
21
|
+
|
|
22
|
+
## Changes in 0.6.2
|
|
23
|
+
|
|
24
|
+
- Namespace startup allows two minutes, with progress every five seconds, instead of failing after 12 seconds on a busy machine. `METALIST_STARTUP_TIMEOUT_SECONDS` can increase the allowance. Failed children are stopped and reaped.
|
|
25
|
+
- Updates preserve the running Python interpreter and validate a disposable installation and namespace startup before stopping live servers. uv then installs the tested package/dependency versions from its cache without network access.
|
|
26
|
+
- Python 3.14 is supported. Command-line legacy imports also work when Python lacks Tk's native GUI component.
|
|
27
|
+
- Release validation now includes the real uv update, backups, and multi-namespace restart on Windows, macOS, and Linux across Python 3.10–3.14.
|
|
28
|
+
|
|
15
29
|
## Changes in 0.6.1
|
|
16
30
|
|
|
17
31
|
- Removed the unused performance overlay, including its menu option and background state updates, fixing a fatal redundant-state error during note rearranging. Obsolete overlay preferences and command usage are discarded automatically.
|
|
@@ -79,15 +93,19 @@ uv tool install metalist
|
|
|
79
93
|
metalist
|
|
80
94
|
```
|
|
81
95
|
|
|
82
|
-
|
|
96
|
+
For a persistent uv installation, you can update from inside MetaList: type **update** in the menu, open **Version Info**, and choose **Update to …** when a newer release is available. Each browser load or refresh asks the server to check PyPI asynchronously. A reminder-style notice links to Version Info the first time a newer version is discovered; the announced version is remembered in that namespace's client preferences. A slow or failed check does not block the app. Version Info can retry a failed check.
|
|
97
|
+
|
|
98
|
+
The in-app updater checks the selected release, creates verified backups, and restarts every namespace. Keep Version Info open to follow progress and reload when ready; closing and reopening it in the same page resumes progress. Signing in again may be necessary. Source checkouts and unmanaged installations still check for releases, but show why in-app installation is unavailable. Update job records and logs live under `~/MetaList/update-jobs/` (or the configured data directory). If installation/restart fails after servers stop, inspect the updater output and run `metalist` on the server to restart it; there is no automatic rollback.
|
|
99
|
+
|
|
100
|
+
You can also update and restart MetaList with one cross-platform command:
|
|
83
101
|
```bash
|
|
84
102
|
metalist update
|
|
85
103
|
```
|
|
86
|
-
The updater checks the installed version against the latest PyPI release first. If MetaList is already current, it reports the installed version and leaves all running namespaces untouched. When an update is available, it checks the
|
|
104
|
+
The updater checks the installed version against the latest PyPI release first. If MetaList is already current, it reports the installed version and leaves all running namespaces untouched. When an update is available, it checks installer prerequisites and uses uv to install the candidate into a disposable tool environment with the current base Python interpreter. It checks dependencies and starts a temporary namespace to verify HTTP readiness, version, and runtime assets. A failed preflight leaves the current installation and running namespaces unchanged. After preflight succeeds, it stops running namespaces, and creates and verifies a new backup of every namespace before installation can begin. Each archive includes the complete notes/settings database, the attachments database when present, and any legacy search-history database. Locked namespaces are backed up with their encrypted data and key metadata intact, without requiring a password.
|
|
87
105
|
|
|
88
106
|
Backups are saved to `~/MetaList/namespaces/<namespace>/backups/<namespace>-<timestamp>.metalist-backup.tar.gz`; the updater prints each verified path. Existing backups remain unchanged and are not pruned. If any backup fails, the update aborts with the current installation intact; run `metalist` to restart the stopped servers after resolving the failure.
|
|
89
107
|
|
|
90
|
-
Only after all backups pass does the updater hand off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock. It
|
|
108
|
+
Only after all backups pass does the updater hand off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock. It delegates installation to uv, pinning the current base interpreter and the exact tested package/dependency versions, using the cache populated during preflight with network access disabled. It then launches MetaList again, and reports the installed version (for example, `MetaList updated to v0.5.0.`). This protection requires an installed version containing the backup safeguard and applies to `metalist update` and the in-app update action; direct pip/uv install commands do not run it.
|
|
91
109
|
|
|
92
110
|
For pip, users can run `pip install metalist`. For a non-editable local install from this checkout, use `uv pip install .` or `pip install .` instead of the editable command below.
|
|
93
111
|
|
|
@@ -247,6 +265,10 @@ convert-from-legacy.py --namespace work --input /path/to/legacy-export.json
|
|
|
247
265
|
|
|
248
266
|
If `--namespace`, `--port`, or `--https-port` are omitted, the import script prompts for them and saves the resulting launch profile inside the target namespace DB. That means a one-time import into `work` can immediately seed later shorthand launches like `metalist work`.
|
|
249
267
|
|
|
268
|
+
Namespace startup allows 120 seconds per child, reports progress every five seconds, and stops/reaps a failed child instead of leaving it running after reporting failure. Set `METALIST_STARTUP_TIMEOUT_SECONDS` to a positive number of seconds to allow more time on a heavily loaded machine. A crashed child still fails immediately. This is a startup allowance, not a guarantee that any machine will finish within two minutes.
|
|
269
|
+
|
|
270
|
+
The preflight is a disposable empty namespace; it cannot guarantee startup against every real database or prevent a later disk/process failure. If installation succeeds but real namespace startup fails, the new package remains installed: resolve the reported startup issue and run `metalist` again. No automatic code/database rollback is attempted, and backups remain unchanged.
|
|
271
|
+
|
|
250
272
|
### Publishing
|
|
251
273
|
For the real user-facing install flow:
|
|
252
274
|
```bash
|
|
@@ -256,15 +278,15 @@ uv tool install metalist
|
|
|
256
278
|
metalist
|
|
257
279
|
```
|
|
258
280
|
|
|
259
|
-
This repo now packages itself under the PyPI distribution name `metalist`. Current releases support Python 3.10 through 3.
|
|
281
|
+
This repo now packages itself under the PyPI distribution name `metalist`. Current releases support Python 3.10 through 3.14.
|
|
260
282
|
|
|
261
283
|
Recommended release path:
|
|
262
284
|
1. In the existing PyPI project `metalist`, configure GitHub Trusted Publishing for `evolvingstuff/metalist` and the workflow file `.github/workflows/publish-pypi.yml`.
|
|
263
|
-
2. Push the candidate commit to a branch and wait for the `Publish to PyPI` validation workflow to pass for that exact commit. GitHub-hosted Windows, macOS, and Linux runners install the built wheel and test application startup outside the source checkout on Python 3.10 through 3.
|
|
285
|
+
2. Push the candidate commit to a branch and wait for the `Publish to PyPI` validation workflow to pass for that exact commit. GitHub-hosted Windows, macOS, and Linux runners install the built wheel and test application startup outside the source checkout on Python 3.10 through 3.14. Every matrix job also exercises the real `metalist update` path with uv, backups, interpreter preservation, and two-namespace restart against a local release index. The old-version fixture carries the candidate updater so this validates the mechanism being shipped; it does not retroactively fix older installed updaters. You do not need those operating systems locally. Archive verification also checks runtime files, including agent Markdown resources, against both the wheel and source distribution.
|
|
264
286
|
3. Only after every required check passes, create and push the release tag for that same commit. Any intervening change requires a new validation run before tagging. Branch and pull-request runs validate without publishing.
|
|
265
287
|
4. The tag workflow repeats validation and publishes the same artifacts tested by its full matrix. Manual publication also requires all checks to pass. Users can then run it with `uvx metalist`, install it persistently with `uv tool install metalist`, or install it with `pip install metalist`.
|
|
266
288
|
|
|
267
|
-
If `--input` is omitted, a file picker opens (when `tkinter`
|
|
289
|
+
If `--input` is omitted, a file picker opens (when `tkinter` and its native `_tkinter` component are available).
|
|
268
290
|
Notes tagged with `@implies` are converted into ontology rules and are not imported as notes. Legacy rules that are invalid under the current ontology grammar are reported and skipped while valid rules continue importing.
|
|
269
291
|
|
|
270
292
|
### Run Tests
|
|
@@ -19,6 +19,7 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
19
19
|
f"{API_PREFIX}/auth/login-namespaces",
|
|
20
20
|
f"{API_PREFIX}/auth/login-namespaces/open",
|
|
21
21
|
f"{API_PREFIX}/auth/status",
|
|
22
|
+
f"{API_PREFIX}/auth/app-update/check",
|
|
22
23
|
f"{API_PREFIX}/auth/session",
|
|
23
24
|
"/favicon.ico",
|
|
24
25
|
"/locked",
|
|
@@ -29,6 +30,7 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
29
30
|
} | ({f"{API_PREFIX}/test/reset"} if TEST_MODE else set()))
|
|
30
31
|
|
|
31
32
|
PUBLIC_PREFIX_PATHS = (
|
|
33
|
+
f"{API_PREFIX}/auth/app-update/jobs/", # Opaque job capability survives restart/logout.
|
|
32
34
|
f"{API_PREFIX}/auth/namespaces/delete-jobs/",
|
|
33
35
|
f"{API_PREFIX}/auth/namespaces/rename-jobs/",
|
|
34
36
|
"/static/", # CSS/JS files needed for login page
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Authenticated in-app update initiation and opaque restart-safe job status."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from fastapi import APIRouter, HTTPException
|
|
5
|
+
import httpx
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
from starlette.background import BackgroundTask
|
|
8
|
+
from starlette.responses import JSONResponse
|
|
9
|
+
|
|
10
|
+
from app.api.transactions import transactional_route
|
|
11
|
+
from app.services import app_updates
|
|
12
|
+
from app.services.exception_capture import CapturedExceptionContext
|
|
13
|
+
from app.services.app_updates import AppUpdateRejected
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
router = APIRouter(prefix='/auth/app-update', tags=['updates'])
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AppUpdateRequest(BaseModel):
|
|
20
|
+
target_version: str = Field(pattern=r'^[0-9]+\.[0-9]+\.[0-9]+$')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@router.get('/check')
|
|
24
|
+
def check_update():
|
|
25
|
+
release = CapturedExceptionContext(httpx.HTTPError,
|
|
26
|
+
boundary='app/api/routes/app_updates.py:check_update:release')
|
|
27
|
+
with release:
|
|
28
|
+
status = app_updates.check_for_update()
|
|
29
|
+
if release.captured_exception is not None:
|
|
30
|
+
raise HTTPException(status_code=503, detail='Could not reach the release server. Please try again.') from release.captured_exception
|
|
31
|
+
return JSONResponse(content=status, headers={'Cache-Control': 'no-store'})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@router.post('')
|
|
35
|
+
@transactional_route
|
|
36
|
+
def start_update(body: AppUpdateRequest):
|
|
37
|
+
admission = CapturedExceptionContext(AppUpdateRejected,
|
|
38
|
+
boundary='app/api/routes/app_updates.py:start_update:admission')
|
|
39
|
+
with admission:
|
|
40
|
+
record = app_updates.create_update_job(body.target_version)
|
|
41
|
+
if admission.captured_exception is not None:
|
|
42
|
+
raise HTTPException(status_code=409, detail=str(admission.captured_exception)) from admission.captured_exception
|
|
43
|
+
return JSONResponse(status_code=202, content=record,
|
|
44
|
+
background=BackgroundTask(app_updates.launch_update_job, record['job_id']))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@router.get('/jobs/{job_id}')
|
|
48
|
+
def update_status(job_id: str):
|
|
49
|
+
lookup = CapturedExceptionContext(AppUpdateRejected, FileNotFoundError,
|
|
50
|
+
boundary='app/api/routes/app_updates.py:update_status:lookup')
|
|
51
|
+
with lookup:
|
|
52
|
+
record = app_updates.read_job(job_id)
|
|
53
|
+
if lookup.captured_exception is not None:
|
|
54
|
+
raise HTTPException(status_code=404, detail='Update job not found') from lookup.captured_exception
|
|
55
|
+
return JSONResponse(content=record, headers={'Cache-Control': 'no-store'})
|
|
@@ -5,6 +5,18 @@ syntax suppressions and is consumed by runtime capture and the startup gate.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
CAPTURE_BOUNDARIES = {
|
|
8
|
+
# In-app updates translate only external transport, installation eligibility and job lookup errors.
|
|
9
|
+
'app/services/app_updates.py:check_for_update:eligibility': ('AppUpdateRejected',),
|
|
10
|
+
'app/services/app_updates.py:job_path:validation': ('ValueError',),
|
|
11
|
+
'app/services/app_updates.py:launch_update_job:launch': ('OSError',),
|
|
12
|
+
'app/api/routes/app_updates.py:check_update:release': ('HTTPError',),
|
|
13
|
+
'app/api/routes/app_updates.py:start_update:admission': ('AppUpdateRejected',),
|
|
14
|
+
'app/api/routes/app_updates.py:update_status:lookup': ('AppUpdateRejected', 'FileNotFoundError'),
|
|
15
|
+
# Release smoke polls only its own disposable HTTP listeners during an actual update.
|
|
16
|
+
'scripts/smoke_self_update.py:_has_updated_namespace:probe_capture': ('HTTPError',),
|
|
17
|
+
# A child can exit between poll and terminate; hung children must be killed and reaped.
|
|
18
|
+
'app/services/namespace_switcher.py:_stop_failed_namespace_launch:terminate_capture': ('ProcessLookupError',),
|
|
19
|
+
'app/services/namespace_switcher.py:_stop_failed_namespace_launch:wait_capture': ('TimeoutExpired',),
|
|
8
20
|
# Translate explicitly validated domain input/resource rejection.
|
|
9
21
|
'app/api/routes/auth.py:_delete_namespace_from_body:delete_capture': ('NamespaceInputRejected', 'FileNotFoundError'),
|
|
10
22
|
# Translate explicitly validated domain input/resource rejection.
|
|
@@ -15,6 +15,9 @@ HOP_HEADERS = frozenset({'connection', 'keep-alive', 'proxy-authenticate', 'prox
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class BoundedProxyServer(ThreadingHTTPServer):
|
|
18
|
+
# Admit a browser's initial connection burst before worker dispatch. The
|
|
19
|
+
# inherited five-entry accept queue can reset the sixth concurrent TLS open.
|
|
20
|
+
request_queue_size = MAX_CONNECTIONS
|
|
18
21
|
daemon_threads = True
|
|
19
22
|
|
|
20
23
|
def __init__(self, *args, **kwargs):
|
|
@@ -46,8 +49,8 @@ def make_proxy_handler(*, backend_host: str, backend_port: int, forward_headers)
|
|
|
46
49
|
protocol_version = 'HTTP/1.1'
|
|
47
50
|
|
|
48
51
|
def _proxy(self):
|
|
49
|
-
#
|
|
50
|
-
|
|
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.
|
|
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 == "
|
|
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)
|