MetaList 0.3.3__tar.gz → 0.3.6__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.3.3/metalist.egg-info → metalist-0.3.6}/PKG-INFO +5 -5
- {metalist-0.3.3 → metalist-0.3.6}/README.md +4 -4
- {metalist-0.3.3 → metalist-0.3.6}/app/api/routes/auth.py +9 -1
- {metalist-0.3.3 → metalist-0.3.6}/app/api/routes/backups.py +96 -31
- {metalist-0.3.3 → metalist-0.3.6}/app/services/namespace_deletion_worker.py +8 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/namespace_switcher.py +10 -0
- metalist-0.3.6/app/services/windows_process_control.py +145 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/css/main.css +0 -13
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/auth.js +23 -3
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/backup-restore-modal.js +151 -83
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/namespace-modals.js +2 -1
- metalist-0.3.6/app/static/js/modules/modals/namespace-ports-profile.js +17 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/version.py +1 -1
- {metalist-0.3.3 → metalist-0.3.6}/convert-from-legacy.py +91 -11
- {metalist-0.3.3 → metalist-0.3.6}/main.py +10 -0
- {metalist-0.3.3 → metalist-0.3.6/metalist.egg-info}/PKG-INFO +5 -5
- {metalist-0.3.3 → metalist-0.3.6}/metalist.egg-info/SOURCES.txt +2 -0
- {metalist-0.3.3 → metalist-0.3.6}/MANIFEST.in +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/__init__.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/__init__.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/deps.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/dev.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/middleware/auth.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/request_auth.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/routes/files.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/routes/mcp.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/routes/memory.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/routes/notes.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/routes/ontology.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/routes/reminders.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/routes/sounds.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/routes/test.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/api/transactions.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/config.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/__init__.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/backup_settings_sql.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/engine.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/file_schema.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/file_session.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/files_sql.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/link_titles_sql.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/migrations.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/notes_sql.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/ontology_rules_sql.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/reminders_sql.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/schema.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/search_history_sql.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/session.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/settings_sql.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/sounds_sql.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/db/tab_state_sql.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/encryption_audit.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/main.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/mcp/__init__.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/mcp/__main__.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/mcp/errors.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/mcp/policy.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/mcp/read_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/mcp/server.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/mcp/tools.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/models/commands.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/models/database.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/models/entities.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/models/enums.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/models/linked_list.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/models/list_operations.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/models/list_traversal.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/models/note_crud.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/models/utils.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/presentation/__init__.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/presentation/render/__init__.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/presentation/render/note_renderer.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/presentation/templates.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/security/encryption.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/server_runtime.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/__init__.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/auth.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/auth_cache_state.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/auth_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/backlinks.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/backup_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/backup_settings_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/base_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/client_state_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/content_cache.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/content_formatting.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/date_filtering.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/dependencies.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/diagnostics.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/embedded_references.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/encryption.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/exception_capture.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/file_registry.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/file_storage.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/html_export.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/html_unformatting.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/hydration_state.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/integrity.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/latex_rendering.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/link_titles.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/login_rate_limit.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/maintenance_mode.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/markdown_rendering.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/memory.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/memory_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/namespace_deletion_jobs.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/note_image_tags.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/note_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/note_store.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/ontology_rules_store.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/query_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/reminders.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/root_sorting.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/runtime_hardening.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/search_history.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/search_index.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/search_query.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/search_text.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/session_timeout_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/shell_session_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/snapshot.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/sound_storage.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/store.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/sync.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/sync_state.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/tab_state.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/tag_case.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/tag_ontology.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/tag_rename.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/tag_suggestions.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/tag_term_matching.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/test_reset.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/tokens.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/transaction_manager.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/undo_service.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/undo_state.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/view_cache.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/view_diff.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/services/view_state.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/startup_js_sanity.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/startup_sanity.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/startup_sanity_config.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/config/command_palette_tags.json +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/css/forms.css +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/img/favicon.ico +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/locked.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/main.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/activity-tracker.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/api-client.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/async-result.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/client-state-api.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/client-state-migration.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/command-palette/click-activation-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/command-palette/command-palette-controller.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/command-palette/endpoint-registry.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/command-palette/preferences-store.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/command-palette/tag-config-loader.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/command-palette/usage-store.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/config.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/connectivity-monitor.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/context-menu/context-menu-registry.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/context-menu/context-menu-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/context-menu/input-caret-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/dom-utils-fixed.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/dom-utils.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/editor-commands.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/editor-selection.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/editor-toolbar.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/error-handler.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/error-overlay.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/location-flags.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/login-namespace-picker.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/alphabetize-root-notes-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/backup-result-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/backup-retention-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/backup-settings-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/base-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/delete-namespace-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/delete-namespace-validation.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/help-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/memory-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/namespace-loading-page.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/ontology-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/password-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/prioritize-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/random-password-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/reminder-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/reset-updated-at-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/session-timeout-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/sound-manager-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/modals/version-info-modal.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/actions/content-actions.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/actions/history-actions.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/actions/note-actions.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/actions/search-actions.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/actions/selection-actions.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/actions/ui-actions.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/events/context-menu-events.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/events/focus-events.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/events/inactivity-events.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/events/input-events.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/events/keyboard-events.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/events/mouse-events.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/events/search-events.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/mode-context.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/mode-logger.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/mode-manager-controller.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/animated-scroll-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/backlinks-panel-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/clipboard-shortcut-policy-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/collapse-affordance-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/collapse-editing-policy-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/command-gate-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/context-menu-target-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/date-filter-indicator-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/date-filter-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/differential-view-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/edit-session-collapse-policy-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/edit-session-collapse-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/embedded-image-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/file-image-preview-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/file-reference-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/filtered-refresh-selection-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/html-paste-sanitizer-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/image-context-menu-action-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/image-file-insert-choice-modal-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/infinite-scroll-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/link-context-menu-action-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/markdown-render-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/note-click-target-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/note-clipboard-write-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/note-collapse-animation-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/note-context-menu-action-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/note-drag-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/note-reposition-animation-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/note-split-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/password-clipboard-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/polling-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/rhs-panel-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/root-date-separator-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/root-sort-indicator-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/root-sort-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/scroll-anchor-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/scroll-restoration-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/scroll-to-top-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/search-contexts-overlay-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/search-debounce-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/search-input-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/search-interaction-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/search-redaction-reveal-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/search-suggestions-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/search-syntax-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/shell-output-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/tab-dom-cache-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/tab-duplication-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/tab-state-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/tag-bar-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/tag-suggestions-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/tag-syntax-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/todo-toggle-editing-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/mode-manager/services/view-mode-search-shortcut-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/password-generator.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/reminder-store.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/reminder-surface-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/selection-markers.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/session-auth.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/sound-service.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/tag-token.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/modules/uuid.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/namespace-deleted.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/js/vendor/markdown-it.min.js +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/static/video/login-startup.mp4 +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/templates/base.html +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/templates/index.html +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/templates/locked.html +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/templates/maintenance.html +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/templates/namespace_deleted.html +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/templates/notes_list.html +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/__init__.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/alphabetize_root_notes.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/base.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/collapse.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/copy_note.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/create_child.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/create_note.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/create_sibling.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/delete_subtree.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/expand.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/indent.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/lock.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/move.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/move_to_top.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/outdent.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/paste_child.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/paste_sibling.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/prioritize.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/record_edit_mode.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/redo.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/rename_tag.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/reset_updated_at.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/run_shell.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/search_comment_autofill.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/search_context_tags.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/set_collapse_bulk.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/set_collapse_in_context.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/split_note.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/toggle_reference_mode.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/toggle_todo_done.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/undo.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/unformat_content.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/update_content.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/usecases/view.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/utils/__init__.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/utils/encryption.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/app/utils/text_utils.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/mcp_client.py +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/metalist.egg-info/dependency_links.txt +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/metalist.egg-info/entry_points.txt +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/metalist.egg-info/requires.txt +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/metalist.egg-info/top_level.txt +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/pyproject.toml +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/scripts/generate-lan-cert.sh +0 -0
- {metalist-0.3.3 → metalist-0.3.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metalist
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
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
|
|
@@ -234,9 +234,9 @@ Tool catalog and schemas:
|
|
|
234
234
|
- `docs/mcp_tools.md`
|
|
235
235
|
|
|
236
236
|
### Legacy Import
|
|
237
|
-
`convert-from-legacy.py` replaces the SQLite database referenced by `app.config.DATABASE_URL
|
|
237
|
+
`convert-from-legacy.py` replaces the SQLite database referenced by `app.config.DATABASE_URL`, clears its files/sounds sidecar, and imports notes from a legacy JSON export.
|
|
238
238
|
|
|
239
|
-
This is destructive. It deletes the existing DB
|
|
239
|
+
This is destructive. It deletes the existing notes DB and related files/sounds DB before rebuilding the namespace.
|
|
240
240
|
|
|
241
241
|
Example usage:
|
|
242
242
|
```bash
|
|
@@ -263,11 +263,11 @@ This repo now packages itself under the PyPI distribution name `metalist`. Curre
|
|
|
263
263
|
|
|
264
264
|
Recommended release path:
|
|
265
265
|
1. In the existing PyPI project `metalist`, configure GitHub Trusted Publishing for `evolvingstuff/metalist` and the workflow file `.github/workflows/publish-pypi.yml`.
|
|
266
|
-
2. Push a tag such as `v0.3.
|
|
266
|
+
2. Push a tag such as `v0.3.5`.
|
|
267
267
|
3. After the GitHub Actions workflow completes, users can run it with `uvx metalist`, install it persistently with `uv tool install metalist`, or install it with `pip install metalist`.
|
|
268
268
|
|
|
269
269
|
If `--input` is omitted, a file picker opens (when `tkinter` is available).
|
|
270
|
-
Notes tagged with `@implies` are converted into ontology rules and are not imported as notes.
|
|
270
|
+
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.
|
|
271
271
|
|
|
272
272
|
### Run Tests
|
|
273
273
|
|
|
@@ -205,9 +205,9 @@ Tool catalog and schemas:
|
|
|
205
205
|
- `docs/mcp_tools.md`
|
|
206
206
|
|
|
207
207
|
### Legacy Import
|
|
208
|
-
`convert-from-legacy.py` replaces the SQLite database referenced by `app.config.DATABASE_URL
|
|
208
|
+
`convert-from-legacy.py` replaces the SQLite database referenced by `app.config.DATABASE_URL`, clears its files/sounds sidecar, and imports notes from a legacy JSON export.
|
|
209
209
|
|
|
210
|
-
This is destructive. It deletes the existing DB
|
|
210
|
+
This is destructive. It deletes the existing notes DB and related files/sounds DB before rebuilding the namespace.
|
|
211
211
|
|
|
212
212
|
Example usage:
|
|
213
213
|
```bash
|
|
@@ -234,11 +234,11 @@ This repo now packages itself under the PyPI distribution name `metalist`. Curre
|
|
|
234
234
|
|
|
235
235
|
Recommended release path:
|
|
236
236
|
1. In the existing PyPI project `metalist`, configure GitHub Trusted Publishing for `evolvingstuff/metalist` and the workflow file `.github/workflows/publish-pypi.yml`.
|
|
237
|
-
2. Push a tag such as `v0.3.
|
|
237
|
+
2. Push a tag such as `v0.3.5`.
|
|
238
238
|
3. After the GitHub Actions workflow completes, users can run it with `uvx metalist`, install it persistently with `uv tool install metalist`, or install it with `pip install metalist`.
|
|
239
239
|
|
|
240
240
|
If `--input` is omitted, a file picker opens (when `tkinter` is available).
|
|
241
|
-
Notes tagged with `@implies` are converted into ontology rules and are not imported as notes.
|
|
241
|
+
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.
|
|
242
242
|
|
|
243
243
|
### Run Tests
|
|
244
244
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
import secrets
|
|
4
5
|
import sqlite3
|
|
5
6
|
from fastapi import APIRouter, Depends, Header, HTTPException, Request, Response
|
|
@@ -88,6 +89,7 @@ from app.services.client_state_service import save_command_palette_usage
|
|
|
88
89
|
|
|
89
90
|
|
|
90
91
|
router = APIRouter(prefix="/auth", tags=["auth2"])
|
|
92
|
+
logger = logging.getLogger(__name__)
|
|
91
93
|
|
|
92
94
|
|
|
93
95
|
class LoginRequest(BaseModel):
|
|
@@ -437,7 +439,13 @@ def _on_hydration_done(future: Future) -> None:
|
|
|
437
439
|
return
|
|
438
440
|
error = future.exception()
|
|
439
441
|
if error is not None:
|
|
440
|
-
|
|
442
|
+
error_message = f"{type(error).__name__}: {error}"
|
|
443
|
+
logger.error(
|
|
444
|
+
"Hydration worker failed: %s",
|
|
445
|
+
error_message,
|
|
446
|
+
exc_info=(type(error), error, error.__traceback__),
|
|
447
|
+
)
|
|
448
|
+
hydration_state.fail(error_message)
|
|
441
449
|
|
|
442
450
|
|
|
443
451
|
def _start_hydration(*, first_load: bool, rebuild_required: bool) -> None:
|
|
@@ -35,6 +35,7 @@ from app.server_runtime import NamespaceLaunchProfile
|
|
|
35
35
|
from app.server_runtime import _DEFAULT_HTTP_PORT
|
|
36
36
|
from app.server_runtime import _DEFAULT_MCP_AGENT_WEB_PORT
|
|
37
37
|
from app.server_runtime import load_all_namespace_launch_profiles
|
|
38
|
+
from app.server_runtime import load_namespace_launch_profile
|
|
38
39
|
from app.server_runtime import resolve_namespace_directory
|
|
39
40
|
from app.server_runtime import resolve_namespaced_database_path
|
|
40
41
|
from app.server_runtime import resolve_namespaces_directory
|
|
@@ -125,6 +126,7 @@ class BackupRestorePreflightResponse(BaseModel):
|
|
|
125
126
|
backup_namespace: str
|
|
126
127
|
target_namespace: str
|
|
127
128
|
same_namespace: bool
|
|
129
|
+
target_is_active: bool
|
|
128
130
|
target_exists: bool
|
|
129
131
|
target_requires_password: bool
|
|
130
132
|
restored_profile: BackupRestoreLaunchProfileResponse | None
|
|
@@ -699,6 +701,22 @@ def _response_profile_from_backup_profile(
|
|
|
699
701
|
)
|
|
700
702
|
|
|
701
703
|
|
|
704
|
+
def _required_existing_target_launch_profile(*, target_namespace: str) -> BackupLaunchProfile:
|
|
705
|
+
profile = load_namespace_launch_profile(namespace=target_namespace)
|
|
706
|
+
if profile is None:
|
|
707
|
+
raise RuntimeError(f"Existing target namespace has no saved launch profile: {target_namespace}")
|
|
708
|
+
if profile.port is None:
|
|
709
|
+
raise RuntimeError(f"Existing target namespace has no saved HTTP port: {target_namespace}")
|
|
710
|
+
if profile.mcp_port is None:
|
|
711
|
+
raise RuntimeError(f"Existing target namespace has no saved MCP port: {target_namespace}")
|
|
712
|
+
return BackupLaunchProfile(
|
|
713
|
+
namespace=target_namespace,
|
|
714
|
+
port=profile.port,
|
|
715
|
+
https_port=profile.https_port,
|
|
716
|
+
mcp_port=profile.mcp_port,
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
|
|
702
720
|
def _backup_profile_from_request(
|
|
703
721
|
*,
|
|
704
722
|
namespace: str,
|
|
@@ -725,6 +743,27 @@ def _assert_import_profile_has_no_internal_overlap(*, profile: BackupLaunchProfi
|
|
|
725
743
|
)
|
|
726
744
|
|
|
727
745
|
|
|
746
|
+
def _restore_import_to_target(
|
|
747
|
+
*,
|
|
748
|
+
backup_path: Path,
|
|
749
|
+
target_database_path: Path,
|
|
750
|
+
backup_namespace: str,
|
|
751
|
+
target_namespace: str,
|
|
752
|
+
launch_profile: BackupLaunchProfile,
|
|
753
|
+
) -> None:
|
|
754
|
+
restore_backup_to_paths_from_namespace(
|
|
755
|
+
backup_path,
|
|
756
|
+
target_database_path,
|
|
757
|
+
source_namespace=backup_namespace,
|
|
758
|
+
)
|
|
759
|
+
save_namespace_launch_profile(
|
|
760
|
+
namespace=target_namespace,
|
|
761
|
+
port=launch_profile.port,
|
|
762
|
+
https_port=launch_profile.https_port,
|
|
763
|
+
mcp_port=launch_profile.mcp_port,
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
|
|
728
767
|
@router.get("/settings", response_model=BackupSettingsResponse)
|
|
729
768
|
def get_backup_settings(token: Annotated[str, Depends(_require_auth)]):
|
|
730
769
|
settings = load_backup_settings(token=token)
|
|
@@ -878,23 +917,32 @@ def restore_backup_preflight(
|
|
|
878
917
|
backup_path,
|
|
879
918
|
expected_namespace=backup_namespace,
|
|
880
919
|
)
|
|
920
|
+
target_exists = _restore_target_exists(target_namespace=target_namespace)
|
|
881
921
|
suggested_profile = None
|
|
882
922
|
port_conflicts: list[str] = []
|
|
883
923
|
if target_namespace != backup_namespace:
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
924
|
+
if target_exists:
|
|
925
|
+
target_profile = _required_existing_target_launch_profile(
|
|
926
|
+
target_namespace=target_namespace,
|
|
927
|
+
)
|
|
928
|
+
suggested_profile = _response_profile_from_backup_profile(profile=target_profile)
|
|
929
|
+
assert suggested_profile is not None
|
|
930
|
+
else:
|
|
931
|
+
port_conflicts = _collect_import_profile_port_conflicts(
|
|
932
|
+
target_namespace=target_namespace,
|
|
933
|
+
launch_profile=restored_profile,
|
|
934
|
+
)
|
|
935
|
+
suggested_profile = _suggest_import_launch_profile(
|
|
936
|
+
target_namespace=target_namespace,
|
|
937
|
+
restored_profile=restored_profile,
|
|
938
|
+
)
|
|
892
939
|
|
|
893
940
|
return BackupRestorePreflightResponse(
|
|
894
941
|
backup_namespace=backup_namespace,
|
|
895
942
|
target_namespace=target_namespace,
|
|
896
943
|
same_namespace=target_namespace == backup_namespace,
|
|
897
|
-
|
|
944
|
+
target_is_active=target_namespace == ACTIVE_NAMESPACE,
|
|
945
|
+
target_exists=target_exists,
|
|
898
946
|
target_requires_password=_target_namespace_requires_password(target_namespace=target_namespace),
|
|
899
947
|
restored_profile=_response_profile_from_backup_profile(profile=restored_profile),
|
|
900
948
|
suggested_profile=suggested_profile,
|
|
@@ -999,27 +1047,44 @@ def import_backup(
|
|
|
999
1047
|
if not backup_path.exists():
|
|
1000
1048
|
raise HTTPException(status_code=404, detail=f"Backup not found: {folder_filename}")
|
|
1001
1049
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1050
|
+
if target_exists:
|
|
1051
|
+
launch_profile = _required_existing_target_launch_profile(
|
|
1052
|
+
target_namespace=target_namespace,
|
|
1053
|
+
)
|
|
1054
|
+
else:
|
|
1055
|
+
launch_profile = _backup_profile_from_request(
|
|
1056
|
+
namespace=target_namespace,
|
|
1057
|
+
profile=payload.launch_profile,
|
|
1058
|
+
)
|
|
1059
|
+
_assert_import_profile_has_no_internal_overlap(profile=launch_profile)
|
|
1060
|
+
_assert_import_profile_ports_do_not_conflict(
|
|
1061
|
+
target_namespace=target_namespace,
|
|
1062
|
+
launch_profile=launch_profile,
|
|
1063
|
+
)
|
|
1011
1064
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1065
|
+
target_is_active = target_namespace == ACTIVE_NAMESPACE
|
|
1066
|
+
if target_is_active:
|
|
1067
|
+
maintenance_service.enter_maintenance("Importing backup")
|
|
1068
|
+
try:
|
|
1069
|
+
_restore_import_to_target(
|
|
1070
|
+
backup_path=backup_path,
|
|
1071
|
+
target_database_path=target_database_path,
|
|
1072
|
+
backup_namespace=backup_namespace,
|
|
1073
|
+
target_namespace=target_namespace,
|
|
1074
|
+
launch_profile=launch_profile,
|
|
1075
|
+
)
|
|
1076
|
+
_reset_runtime_state_after_restore()
|
|
1077
|
+
finally:
|
|
1078
|
+
maintenance_service.exit_maintenance()
|
|
1079
|
+
_schedule_server_restart_after_restore(delay_seconds=0.5)
|
|
1080
|
+
else:
|
|
1081
|
+
_restore_import_to_target(
|
|
1082
|
+
backup_path=backup_path,
|
|
1083
|
+
target_database_path=target_database_path,
|
|
1084
|
+
backup_namespace=backup_namespace,
|
|
1085
|
+
target_namespace=target_namespace,
|
|
1086
|
+
launch_profile=launch_profile,
|
|
1087
|
+
)
|
|
1023
1088
|
|
|
1024
1089
|
return BackupRestoreResponse(
|
|
1025
1090
|
backup_id=payload.backup_id,
|
|
@@ -1027,7 +1092,7 @@ def import_backup(
|
|
|
1027
1092
|
backup_filename=payload.backup_filename,
|
|
1028
1093
|
backup_namespace=backup_namespace,
|
|
1029
1094
|
target_namespace=target_namespace,
|
|
1030
|
-
active_namespace_restarted=
|
|
1031
|
-
open_namespace_suggested=
|
|
1095
|
+
active_namespace_restarted=target_is_active,
|
|
1096
|
+
open_namespace_suggested=not target_is_active,
|
|
1032
1097
|
message="Backup imported successfully",
|
|
1033
1098
|
)
|
|
@@ -5,6 +5,7 @@ import os
|
|
|
5
5
|
import shutil
|
|
6
6
|
import signal
|
|
7
7
|
import subprocess
|
|
8
|
+
import sys
|
|
8
9
|
import time
|
|
9
10
|
import traceback
|
|
10
11
|
|
|
@@ -14,6 +15,8 @@ from app.server_runtime import validate_namespace
|
|
|
14
15
|
from app.services.exception_capture import CapturedExceptionContext
|
|
15
16
|
from app.services.namespace_deletion_jobs import mark_namespace_deletion_job_failed
|
|
16
17
|
from app.services.namespace_deletion_jobs import mark_namespace_deletion_job_succeeded
|
|
18
|
+
from app.services.windows_process_control import is_process_running as is_windows_process_running
|
|
19
|
+
from app.services.windows_process_control import stop_process as stop_windows_process
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
_WAIT_POLL_INTERVAL_SECONDS = 0.25
|
|
@@ -50,6 +53,8 @@ def _is_process_running(*, pid: int) -> bool:
|
|
|
50
53
|
raise TypeError(f"pid must be an int, got {type(pid)}")
|
|
51
54
|
if pid <= 0:
|
|
52
55
|
raise ValueError(f"pid must be positive, got: {pid}")
|
|
56
|
+
if sys.platform == "win32":
|
|
57
|
+
return is_windows_process_running(pid=pid)
|
|
53
58
|
kill_capture = CapturedExceptionContext(ProcessLookupError, PermissionError)
|
|
54
59
|
with kill_capture:
|
|
55
60
|
os.kill(pid, 0)
|
|
@@ -92,6 +97,9 @@ def _send_signal_if_running(*, pid: int, signal_number: int) -> None:
|
|
|
92
97
|
|
|
93
98
|
|
|
94
99
|
def _stop_process(*, pid: int) -> None:
|
|
100
|
+
if sys.platform == "win32":
|
|
101
|
+
stop_windows_process(pid=pid)
|
|
102
|
+
return
|
|
95
103
|
if _wait_for_process_exit(pid=pid, timeout_seconds=_INITIAL_EXIT_GRACE_SECONDS):
|
|
96
104
|
return
|
|
97
105
|
|
|
@@ -30,6 +30,9 @@ from app.server_runtime import validate_namespace
|
|
|
30
30
|
from app.services.diagnostics import recycle_direct_append_log_file
|
|
31
31
|
from app.services.exception_capture import CapturedExceptionContext
|
|
32
32
|
from app.services.namespace_deletion_jobs import create_namespace_deletion_job
|
|
33
|
+
from app.services.windows_process_control import find_listening_pids_for_port as find_windows_listening_pids_for_port
|
|
34
|
+
from app.services.windows_process_control import is_process_running as is_windows_process_running
|
|
35
|
+
from app.services.windows_process_control import stop_process as stop_windows_process
|
|
33
36
|
|
|
34
37
|
|
|
35
38
|
_PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
@@ -1086,6 +1089,8 @@ def _is_process_running(*, pid: int) -> bool:
|
|
|
1086
1089
|
raise TypeError(f"pid must be an int, got {type(pid)}")
|
|
1087
1090
|
if pid <= 0:
|
|
1088
1091
|
raise ValueError(f"pid must be positive, got: {pid}")
|
|
1092
|
+
if sys.platform == "win32":
|
|
1093
|
+
return is_windows_process_running(pid=pid)
|
|
1089
1094
|
kill_capture = CapturedExceptionContext(ProcessLookupError, PermissionError)
|
|
1090
1095
|
with kill_capture:
|
|
1091
1096
|
os.kill(pid, 0)
|
|
@@ -1128,6 +1133,9 @@ def _send_signal_if_running(*, pid: int, signal_number: int) -> None:
|
|
|
1128
1133
|
|
|
1129
1134
|
|
|
1130
1135
|
def _stop_process(*, pid: int) -> None:
|
|
1136
|
+
if sys.platform == "win32":
|
|
1137
|
+
stop_windows_process(pid=pid)
|
|
1138
|
+
return
|
|
1131
1139
|
if not _is_process_running(pid=pid):
|
|
1132
1140
|
return
|
|
1133
1141
|
|
|
@@ -1147,6 +1155,8 @@ def _find_listening_pids_for_port(*, port: int) -> list[int]:
|
|
|
1147
1155
|
raise TypeError(f"port must be an int, got {type(port)}")
|
|
1148
1156
|
if port <= 0 or port > 65535:
|
|
1149
1157
|
raise ValueError(f"port must be between 1 and 65535, got: {port}")
|
|
1158
|
+
if sys.platform == "win32":
|
|
1159
|
+
return find_windows_listening_pids_for_port(port=port)
|
|
1150
1160
|
|
|
1151
1161
|
lsof_path = shutil.which("lsof")
|
|
1152
1162
|
if lsof_path is None:
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
_WAIT_POLL_INTERVAL_SECONDS = 0.25
|
|
9
|
+
_TERMINATE_GRACE_SECONDS = 5.0
|
|
10
|
+
_KILL_GRACE_SECONDS = 5.0
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _validate_pid(*, pid: int) -> None:
|
|
14
|
+
if not isinstance(pid, int):
|
|
15
|
+
raise TypeError(f"pid must be an int, got {type(pid)}")
|
|
16
|
+
if pid <= 0:
|
|
17
|
+
raise ValueError(f"pid must be positive, got: {pid}")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _validate_port(*, port: int) -> None:
|
|
21
|
+
if not isinstance(port, int):
|
|
22
|
+
raise TypeError(f"port must be an int, got {type(port)}")
|
|
23
|
+
if port <= 0 or port > 65535:
|
|
24
|
+
raise ValueError(f"port must be between 1 and 65535, got: {port}")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _resolve_powershell_path() -> str:
|
|
28
|
+
powershell_path = shutil.which("powershell.exe")
|
|
29
|
+
if powershell_path is not None:
|
|
30
|
+
return powershell_path
|
|
31
|
+
pwsh_path = shutil.which("pwsh.exe")
|
|
32
|
+
if pwsh_path is not None:
|
|
33
|
+
return pwsh_path
|
|
34
|
+
raise RuntimeError("PowerShell is required for MetaList process management on Windows")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _run_powershell(*, script: str, operation: str) -> str:
|
|
38
|
+
if script.strip() == "":
|
|
39
|
+
raise ValueError("PowerShell script must not be empty")
|
|
40
|
+
if operation.strip() == "":
|
|
41
|
+
raise ValueError("PowerShell operation must not be empty")
|
|
42
|
+
completed = subprocess.run(
|
|
43
|
+
[
|
|
44
|
+
_resolve_powershell_path(),
|
|
45
|
+
"-NoLogo",
|
|
46
|
+
"-NoProfile",
|
|
47
|
+
"-NonInteractive",
|
|
48
|
+
"-Command",
|
|
49
|
+
script,
|
|
50
|
+
],
|
|
51
|
+
capture_output=True,
|
|
52
|
+
check=False,
|
|
53
|
+
text=True,
|
|
54
|
+
)
|
|
55
|
+
if completed.returncode != 0:
|
|
56
|
+
raise RuntimeError(
|
|
57
|
+
f"PowerShell failed while {operation}: "
|
|
58
|
+
f"exit={completed.returncode} stderr={completed.stderr.strip()!r}"
|
|
59
|
+
)
|
|
60
|
+
return completed.stdout.strip()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def find_listening_pids_for_port(*, port: int) -> list[int]:
|
|
64
|
+
_validate_port(port=port)
|
|
65
|
+
stdout = _run_powershell(
|
|
66
|
+
script=(
|
|
67
|
+
f"@(Get-NetTCPConnection -State Listen -LocalPort {port} -ErrorAction SilentlyContinue "
|
|
68
|
+
"| Select-Object -ExpandProperty OwningProcess) -join [Environment]::NewLine"
|
|
69
|
+
),
|
|
70
|
+
operation=f"checking port {port}",
|
|
71
|
+
)
|
|
72
|
+
if stdout == "":
|
|
73
|
+
return []
|
|
74
|
+
|
|
75
|
+
ordered_pids: list[int] = []
|
|
76
|
+
seen_pids: set[int] = set()
|
|
77
|
+
for raw_line in stdout.splitlines():
|
|
78
|
+
raw_pid = raw_line.strip()
|
|
79
|
+
if not raw_pid.isdigit():
|
|
80
|
+
raise RuntimeError(
|
|
81
|
+
f"PowerShell returned a non-numeric listener pid for port {port}: {raw_pid!r}"
|
|
82
|
+
)
|
|
83
|
+
pid = int(raw_pid)
|
|
84
|
+
if pid <= 0:
|
|
85
|
+
raise RuntimeError(f"PowerShell returned invalid listener pid for port {port}: {pid}")
|
|
86
|
+
if pid in seen_pids:
|
|
87
|
+
continue
|
|
88
|
+
seen_pids.add(pid)
|
|
89
|
+
ordered_pids.append(pid)
|
|
90
|
+
return ordered_pids
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def is_process_running(*, pid: int) -> bool:
|
|
94
|
+
_validate_pid(pid=pid)
|
|
95
|
+
stdout = _run_powershell(
|
|
96
|
+
script=(
|
|
97
|
+
f"@(Get-Process -Id {pid} -ErrorAction SilentlyContinue "
|
|
98
|
+
"| Select-Object -ExpandProperty Id) -join [Environment]::NewLine"
|
|
99
|
+
),
|
|
100
|
+
operation=f"checking process {pid}",
|
|
101
|
+
)
|
|
102
|
+
if stdout == "":
|
|
103
|
+
return False
|
|
104
|
+
if stdout != str(pid):
|
|
105
|
+
raise RuntimeError(f"PowerShell returned an unexpected process id for pid {pid}: {stdout!r}")
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _wait_for_process_exit(*, pid: int, timeout_seconds: float) -> bool:
|
|
110
|
+
if not isinstance(timeout_seconds, float):
|
|
111
|
+
raise TypeError(f"timeout_seconds must be a float, got {type(timeout_seconds)}")
|
|
112
|
+
if timeout_seconds < 0.0:
|
|
113
|
+
raise ValueError(f"timeout_seconds must be >= 0.0, got {timeout_seconds}")
|
|
114
|
+
|
|
115
|
+
deadline = time.monotonic() + timeout_seconds
|
|
116
|
+
while time.monotonic() < deadline:
|
|
117
|
+
if not is_process_running(pid=pid):
|
|
118
|
+
return True
|
|
119
|
+
time.sleep(_WAIT_POLL_INTERVAL_SECONDS)
|
|
120
|
+
return not is_process_running(pid=pid)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def stop_process(*, pid: int) -> None:
|
|
124
|
+
_validate_pid(pid=pid)
|
|
125
|
+
if not is_process_running(pid=pid):
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
_run_powershell(
|
|
129
|
+
script=f"Stop-Process -Id {pid} -ErrorAction Stop",
|
|
130
|
+
operation=f"stopping process {pid}",
|
|
131
|
+
)
|
|
132
|
+
if _wait_for_process_exit(pid=pid, timeout_seconds=_TERMINATE_GRACE_SECONDS):
|
|
133
|
+
return
|
|
134
|
+
if not is_process_running(pid=pid):
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
_run_powershell(
|
|
138
|
+
script=f"Stop-Process -Id {pid} -Force -ErrorAction Stop",
|
|
139
|
+
operation=f"force-stopping process {pid}",
|
|
140
|
+
)
|
|
141
|
+
if _wait_for_process_exit(pid=pid, timeout_seconds=_KILL_GRACE_SECONDS):
|
|
142
|
+
return
|
|
143
|
+
if not is_process_running(pid=pid):
|
|
144
|
+
return
|
|
145
|
+
raise RuntimeError(f"Timed out waiting for Windows process {pid} to exit")
|
|
@@ -1472,19 +1472,6 @@ body.ontology-modal-open #ontology-modal {
|
|
|
1472
1472
|
font-weight: 700;
|
|
1473
1473
|
}
|
|
1474
1474
|
|
|
1475
|
-
.backup-restore-port-config {
|
|
1476
|
-
margin: 16px 0;
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
.backup-restore-port-config h4 {
|
|
1480
|
-
margin: 0 0 8px;
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
.backup-restore-port-config ul {
|
|
1484
|
-
margin: 8px 0 0;
|
|
1485
|
-
padding-left: 22px;
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
1475
|
.backup-retention-modal-content {
|
|
1489
1476
|
width: min(760px, 92vw);
|
|
1490
1477
|
}
|
|
@@ -533,7 +533,13 @@ export const Auth = {
|
|
|
533
533
|
if (typeof status.error === 'string' && status.error.length > 0) {
|
|
534
534
|
throw new Error(status.error);
|
|
535
535
|
}
|
|
536
|
-
|
|
536
|
+
const failedPhase = typeof status.phase === 'string' && status.phase.length > 0
|
|
537
|
+
? status.phase
|
|
538
|
+
: 'unknown phase';
|
|
539
|
+
const failedMessage = typeof status.message === 'string' && status.message.length > 0
|
|
540
|
+
? status.message
|
|
541
|
+
: 'server returned no error detail';
|
|
542
|
+
throw new Error(`Hydration failed during ${failedPhase}: ${failedMessage}`);
|
|
537
543
|
}
|
|
538
544
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
539
545
|
const pollResponse = await fetch(CONFIG.API.AUTH.HYDRATION_STATUS, { headers });
|
|
@@ -680,6 +686,7 @@ export const Auth = {
|
|
|
680
686
|
}
|
|
681
687
|
|
|
682
688
|
const responseText = await response.text();
|
|
689
|
+
let startupPhase = 'reading the login response';
|
|
683
690
|
try {
|
|
684
691
|
const data = JSON.parse(responseText);
|
|
685
692
|
|
|
@@ -691,28 +698,41 @@ export const Auth = {
|
|
|
691
698
|
if (data.hydration_required !== true) {
|
|
692
699
|
throw new Error('Login response must require the progress flow');
|
|
693
700
|
}
|
|
701
|
+
startupPhase = 'hydrating the workspace';
|
|
694
702
|
await this._runHydrationFlow();
|
|
695
703
|
|
|
696
704
|
console.log('[Auth] Login successful, initializing ModeManager');
|
|
697
705
|
if (window.ModeManager) {
|
|
706
|
+
startupPhase = 'initializing the workspace UI';
|
|
698
707
|
await window.ModeManager.init({});
|
|
708
|
+
startupPhase = 'initializing the command palette';
|
|
699
709
|
await CommandPalette.init();
|
|
710
|
+
startupPhase = 'finishing the startup introduction';
|
|
700
711
|
await this.waitForStartupIntro();
|
|
712
|
+
startupPhase = 'revealing the workspace';
|
|
701
713
|
this.revealMainApp();
|
|
714
|
+
startupPhase = 'starting reminders';
|
|
702
715
|
await ReminderSurface.start();
|
|
703
716
|
document.body.dataset.appReady = 'true';
|
|
704
717
|
} else {
|
|
718
|
+
startupPhase = 'reloading the workspace';
|
|
705
719
|
window.location.reload();
|
|
706
720
|
}
|
|
707
721
|
} catch (error) {
|
|
708
722
|
document.body.classList.remove('loading');
|
|
709
723
|
this._setLoginLoadingTitle('Workspace startup failed');
|
|
710
724
|
const loadingMessage = this._requireElement('login-loading-message');
|
|
711
|
-
|
|
725
|
+
const errorMessage = error instanceof Error
|
|
726
|
+
? `${error.name}: ${error.message}`
|
|
727
|
+
: `Non-Error thrown: ${String(error)}`;
|
|
728
|
+
const diagnosticMessage = `${startupPhase}: ${errorMessage}`;
|
|
729
|
+
loadingMessage.textContent =
|
|
730
|
+
`Your password was accepted, but the workspace could not finish opening. ${diagnosticMessage}`;
|
|
731
|
+
window.alert(`MetaList workspace startup failed:\n\n${diagnosticMessage}`);
|
|
712
732
|
if (error instanceof Error) {
|
|
713
733
|
throw error;
|
|
714
734
|
}
|
|
715
|
-
throw new Error(
|
|
735
|
+
throw new Error(errorMessage);
|
|
716
736
|
}
|
|
717
737
|
document.body.classList.remove('loading');
|
|
718
738
|
},
|