topos-node 0.1.6__tar.gz → 0.1.8__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.
- {topos_node-0.1.6 → topos_node-0.1.8}/PKG-INFO +1 -1
- {topos_node-0.1.6 → topos_node-0.1.8}/pyproject.toml +1 -1
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/__version__.py +2 -2
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/device.py +2 -2
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/control_plane_client.py +19 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/api_models.py +1 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/handlers.py +12 -1
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/state.py +58 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/interfaces.py +1 -1
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/local.py +6 -2
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/postgres.py +68 -5
- {topos_node-0.1.6 → topos_node-0.1.8}/topos_node.egg-info/PKG-INFO +1 -1
- {topos_node-0.1.6 → topos_node-0.1.8}/LICENSE +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/README.md +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/setup.cfg +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/shared/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/shared/filtering.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/shared/schema_registry.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/duckdb_adapter.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/messenger_communities.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/messenger_graph.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/messenger_labels.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/profiles.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/query_engine.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/raw_queries.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/analytics.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/app_registry.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/backup.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/compute_remote.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/data_commit.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/data_explorer_table_prefs.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/db.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/enrichment.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/filter_lab.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/health.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/ingestion_api.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/ingestion_compat.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/ingestion_sources.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/llm.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/local_mcp.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/messenger_analytics.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/query_api.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/sanitization_ollama_config.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/source_install.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/sources.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/sync.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/ui_config.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/uma_data.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/usage.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/user_identity.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/app.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/auth.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/mappers/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/mappers/base.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/mappers/chatgpt_mapper.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/mappers/grok_mapper.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/mappers/messenger_mapper.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/models.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/resolver.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/cli/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/cli/__main__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/cli/commands.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/config/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/config/sanitization_ollama.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/config/settings.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/contacts/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/contacts/identity.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/connection_resilience.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/device_helpers.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/errors.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/events.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/logging.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/metrics.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/startup_banner.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/table_layers.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/types.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/data_explorer_table_prefs.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/backends/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/backends/base.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/backends/huggingface.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/backends/ollama.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/backends/stub.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/engine.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/intake.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/queue_manager.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/registration.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/result_formatter.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/router.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/scoped_token.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/tasks.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/transport.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/usage_guard.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/usage_observation.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/validator.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/derived_tables.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/base.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/embeddings_job.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/emo_27_job.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/entities_job.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/sentiment_job.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/topics_job.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/raw/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/raw/attachments_job.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/raw/language_job.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/raw/time_normalization_job.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/raw/tool_calls_job.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/models/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/models/manager.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/models/registry.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/models/versioning.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/orchestrator.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/processor.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/progress_bar.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/website_classifier.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/bundles.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/schema.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/service.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/worker.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/hosted_pool_lease.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/checkpoints/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/checkpoints/checkpoint_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/checkpoints/sqlite_checkpoint_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/ingest_helpers.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/jobs.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/local_sync.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/log_preview.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/manager.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parser.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/base.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/browser_parser.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/calendar_parser.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/chatgpt_conversation_flattener.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/chatgpt_parser.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/grok_parser.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/messenger_parser.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/progress.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/base.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/calendar.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/chatgpt.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/contact_importers.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/grok.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/imessage_reader.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/signal_export_parser.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/signal_reader.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/state_machine.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/triggers/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/triggers/file_trigger.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/triggers/sqlite_trigger.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/validation/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/validation/base.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/validation/schema_registry.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/validation/schema_validator.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/lineage/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/lineage/provenance.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/lineage/tracker.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/mcp_stdio_proxy.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/observability/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/observability/alerts.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/observability/metrics.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/observability/tracing.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/openai_client.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/projections/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/projections/vector_index/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/projections/vector_index/base.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/projections/vector_index/builders.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/projections/vector_index/health_checks.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/rate_limit.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/sanitization/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/sanitization/ollama_transforms.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/scope_resolution.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/container.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/embeddings/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/embeddings/base.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/embeddings/local.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/embeddings/remote.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/llm/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/llm/base.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/llm/openai.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/sources/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/sources/definitions.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/sources/install_service.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/sources/registry.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/sources/runtime_install.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/startup_banner.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/ai_chat/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/ai_chat/canonicalizer.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/ai_chat/mapper.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/ai_chat/model.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/ai_chat/tables.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/canonical_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/conversations_tables.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/mapping_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/postgres.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/client.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/migrations/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/migrations/stage9_column_renames.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/paths.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/postgres.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/schema.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/enrichment/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/enrichment/canonical_enrichment_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/enrichment/raw_enrichment_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/normalized/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/normalized/normalized_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/oplog/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/oplog/decision.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/oplog/oplog_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/oplog/postgres.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/projections/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/projections/index_ops_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/projections/vector_index_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/browser_flat_tables.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/file_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/raw_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/raw_tables_manager.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/sqlite_raw_store.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/security/encryption.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/signal_identity.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/source_settings.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/user_identity.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/sync/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/sync/client.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/sync_handlers.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/testing/__init__.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/testing/lifespan.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/uma_contact_enrichment.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/uma_filters.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/uma_resource_id.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/uma_rpt.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/utils/base_object.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos/websocket_client.py +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos_node.egg-info/SOURCES.txt +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos_node.egg-info/dependency_links.txt +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos_node.egg-info/entry_points.txt +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos_node.egg-info/requires.txt +0 -0
- {topos_node-0.1.6 → topos_node-0.1.8}/topos_node.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "topos-node"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.8"
|
|
8
8
|
description = "Topos personal AI engine (FastAPI): local data, sync, control plane WebSocket client"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -12,12 +12,12 @@ router = APIRouter()
|
|
|
12
12
|
|
|
13
13
|
@router.get("/device/info", response_model=DeviceInfoResponse, dependencies=[Depends(require_api_key)])
|
|
14
14
|
async def get_device_info(services: Services = Depends(get_services)):
|
|
15
|
-
return await services.device.get_device_info()
|
|
15
|
+
return await services.device.get_device_info(context=None)
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@router.get("/device_info", response_model=DeviceInfoResponse, dependencies=[Depends(require_api_key)])
|
|
19
19
|
async def get_device_info_alias(services: Services = Depends(get_services)):
|
|
20
|
-
return await services.device.get_device_info()
|
|
20
|
+
return await services.device.get_device_info(context=None)
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
@router.post("/device_name", response_model=DeviceNameResponse, dependencies=[Depends(require_api_key)])
|
|
@@ -103,6 +103,24 @@ class ControlPlaneClient:
|
|
|
103
103
|
self._task = asyncio.create_task(self._run())
|
|
104
104
|
logger.info("Control plane client starting: %s", self.control_plane_url)
|
|
105
105
|
|
|
106
|
+
def _restart_if_task_stopped(self) -> None:
|
|
107
|
+
# Self-heal if the reconnect loop exited unexpectedly while the app is still running.
|
|
108
|
+
if self._stop.is_set() or not self._task or not self._task.done():
|
|
109
|
+
return
|
|
110
|
+
reason = "unknown"
|
|
111
|
+
try:
|
|
112
|
+
exc = self._task.exception()
|
|
113
|
+
if exc is not None:
|
|
114
|
+
reason = repr(exc)
|
|
115
|
+
except asyncio.CancelledError:
|
|
116
|
+
reason = "cancelled"
|
|
117
|
+
logger.warning(
|
|
118
|
+
"Control plane background task stopped unexpectedly; restarting endpoint=%s reason=%s",
|
|
119
|
+
self.control_plane_url,
|
|
120
|
+
reason,
|
|
121
|
+
)
|
|
122
|
+
self.start()
|
|
123
|
+
|
|
106
124
|
async def wait_until_connected(self, timeout_s: float | None = None) -> bool:
|
|
107
125
|
timeout = float(timeout_s) if timeout_s is not None else float(settings.connection_readiness_timeout_seconds)
|
|
108
126
|
try:
|
|
@@ -292,6 +310,7 @@ class ControlPlaneClient:
|
|
|
292
310
|
async def send_message(self, message: Dict[str, Any]) -> None:
|
|
293
311
|
"""Send an unsolicited message to the control plane (e.g., progress updates)."""
|
|
294
312
|
if not self._ws:
|
|
313
|
+
self._restart_if_task_stopped()
|
|
295
314
|
await self._enqueue_presence_message(message)
|
|
296
315
|
logger.warning("Queued presence message; control plane currently disconnected")
|
|
297
316
|
return
|
|
@@ -1139,6 +1139,15 @@ async def handle_control_plane_request(message: Dict[str, Any]) -> Optional[Dict
|
|
|
1139
1139
|
_payload = message.get("payload") or {}
|
|
1140
1140
|
_mcp_source = _payload.get("mcp_source")
|
|
1141
1141
|
_mcp_requester_id = _payload.get("mcp_requester_id")
|
|
1142
|
+
if msg_type == "migrate_browser_plugin_app_id":
|
|
1143
|
+
from .state import _migrate_legacy_browser_plugin_app_ids
|
|
1144
|
+
|
|
1145
|
+
conn = get_db_connection()
|
|
1146
|
+
if not conn:
|
|
1147
|
+
return {"id": req_id, "status": "error", "error": "Database not available"}
|
|
1148
|
+
updated = _migrate_legacy_browser_plugin_app_ids(conn)
|
|
1149
|
+
return {"id": req_id, "status": "ok", "payload": {"updated_rows": updated}}
|
|
1150
|
+
|
|
1142
1151
|
if msg_type == "get_request_counts":
|
|
1143
1152
|
"""Return UMA + MCP request counts from engine DB (for CP proxy or direct frontend)."""
|
|
1144
1153
|
payload = message.get("payload") or {}
|
|
@@ -1759,8 +1768,10 @@ async def handle_control_plane_request(message: Dict[str, Any]) -> Optional[Dict
|
|
|
1759
1768
|
except Exception as exc: # noqa: BLE001
|
|
1760
1769
|
return {"id": req_id, "status": "error", "error": str(exc)}
|
|
1761
1770
|
if msg_type == "get_device_info":
|
|
1771
|
+
payload = message.get("payload") or {}
|
|
1772
|
+
context = payload if isinstance(payload, dict) else {}
|
|
1762
1773
|
try:
|
|
1763
|
-
result = await get_services().device.get_device_info()
|
|
1774
|
+
result = await get_services().device.get_device_info(context=context)
|
|
1764
1775
|
return {"id": req_id, "status": "ok", "payload": result.model_dump()}
|
|
1765
1776
|
except Exception as exc: # noqa: BLE001
|
|
1766
1777
|
return {"id": req_id, "status": "error", "error": str(exc)}
|
|
@@ -14,6 +14,8 @@ from typing import Any, Dict, Optional
|
|
|
14
14
|
|
|
15
15
|
MCP_REQUEST_LOG_TABLE = "mcp_request_log"
|
|
16
16
|
UMA_ACCESS_REQUESTS_TABLE = "uma_access_requests"
|
|
17
|
+
LEGACY_BROWSER_PLUGIN_APP_ID = "browser-plugin"
|
|
18
|
+
BROWSER_HISTORY_PLUGIN_APP_ID = "browser-history-plugin"
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
def derive_uma_access_context(owner_user_id: str, requesting_user_id: Optional[str]) -> str:
|
|
@@ -401,6 +403,29 @@ def _ensure_uma_access_requests_table(conn: sqlite3.Connection) -> None:
|
|
|
401
403
|
conn.commit()
|
|
402
404
|
except Exception as exc:
|
|
403
405
|
logger.warning("Failed to ensure uma_access_requests table exists: %s", exc)
|
|
406
|
+
else:
|
|
407
|
+
_migrate_legacy_browser_plugin_app_ids(conn)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _migrate_legacy_browser_plugin_app_ids(conn: sqlite3.Connection) -> int:
|
|
411
|
+
"""Rewrite engine Activity counts from browser-plugin → browser-history-plugin (idempotent)."""
|
|
412
|
+
try:
|
|
413
|
+
cursor = conn.execute(
|
|
414
|
+
f"UPDATE {UMA_ACCESS_REQUESTS_TABLE} SET app_id = ? WHERE app_id = ?",
|
|
415
|
+
(BROWSER_HISTORY_PLUGIN_APP_ID, LEGACY_BROWSER_PLUGIN_APP_ID),
|
|
416
|
+
)
|
|
417
|
+
if cursor.rowcount:
|
|
418
|
+
conn.commit()
|
|
419
|
+
logger.info(
|
|
420
|
+
"Migrated %s uma_access_requests rows: %s → %s",
|
|
421
|
+
cursor.rowcount,
|
|
422
|
+
LEGACY_BROWSER_PLUGIN_APP_ID,
|
|
423
|
+
BROWSER_HISTORY_PLUGIN_APP_ID,
|
|
424
|
+
)
|
|
425
|
+
return int(cursor.rowcount or 0)
|
|
426
|
+
except Exception as exc:
|
|
427
|
+
logger.warning("browser-plugin app_id migration on engine failed: %s", exc)
|
|
428
|
+
return 0
|
|
404
429
|
|
|
405
430
|
|
|
406
431
|
def record_uma_request(
|
|
@@ -463,6 +488,7 @@ def get_uma_request_counts(
|
|
|
463
488
|
"total_read_requests": 0,
|
|
464
489
|
"total_write_requests": 0,
|
|
465
490
|
"by_app": [],
|
|
491
|
+
"by_app_requester": [],
|
|
466
492
|
"by_requesting_user": [],
|
|
467
493
|
"access_attribution": {
|
|
468
494
|
"window_days": since_days or 0,
|
|
@@ -510,6 +536,38 @@ def get_uma_request_counts(
|
|
|
510
536
|
for aid, d in sorted(by_app.items())
|
|
511
537
|
]
|
|
512
538
|
|
|
539
|
+
cursor = conn.execute(
|
|
540
|
+
f"""SELECT app_id, requesting_user_id, requesting_user_email, request_type
|
|
541
|
+
FROM {UMA_ACCESS_REQUESTS_TABLE}
|
|
542
|
+
WHERE owner_user_id = ? AND created_at >= ?""",
|
|
543
|
+
(owner_user_id, since_ts),
|
|
544
|
+
)
|
|
545
|
+
by_app_req: Dict[str, Dict[str, Any]] = {}
|
|
546
|
+
for row in cursor.fetchall():
|
|
547
|
+
app_id_val = (row[0] or "").strip() if len(row) > 0 else ""
|
|
548
|
+
rid = (row[1] or "").strip() if len(row) > 1 else ""
|
|
549
|
+
remail = (row[2] or "").strip() if len(row) > 2 else ""
|
|
550
|
+
rt = (row[3] or "").strip().lower() if len(row) > 3 else ""
|
|
551
|
+
key = f"{app_id_val}\0{rid}\0{remail}"
|
|
552
|
+
if key not in by_app_req:
|
|
553
|
+
by_app_req[key] = {
|
|
554
|
+
"app_id": app_id_val or None,
|
|
555
|
+
"requesting_user_id": rid or None,
|
|
556
|
+
"requesting_user_email": remail or None,
|
|
557
|
+
"read_requests": 0,
|
|
558
|
+
"write_requests": 0,
|
|
559
|
+
}
|
|
560
|
+
if rt == "read":
|
|
561
|
+
by_app_req[key]["read_requests"] += 1
|
|
562
|
+
elif rt == "write":
|
|
563
|
+
by_app_req[key]["write_requests"] += 1
|
|
564
|
+
app_req_ranked = []
|
|
565
|
+
for d in by_app_req.values():
|
|
566
|
+
tr = int(d["read_requests"]) + int(d["write_requests"])
|
|
567
|
+
app_req_ranked.append({**d, "total_requests": tr})
|
|
568
|
+
app_req_ranked.sort(key=lambda x: -x["total_requests"])
|
|
569
|
+
out["by_app_requester"] = app_req_ranked[:50]
|
|
570
|
+
|
|
513
571
|
cursor = conn.execute(
|
|
514
572
|
f"""SELECT requesting_user_id, requesting_user_email, request_type, access_context
|
|
515
573
|
FROM {UMA_ACCESS_REQUESTS_TABLE}
|
|
@@ -31,7 +31,7 @@ class SyncService(Protocol):
|
|
|
31
31
|
class DeviceService(Protocol):
|
|
32
32
|
async def get_pairing_code(self) -> PairingCodeResponse: ...
|
|
33
33
|
async def pair_device(self, pairing_code: str, keep_existing_data: bool) -> PairDeviceResponse: ...
|
|
34
|
-
async def get_device_info(self) -> DeviceInfoResponse: ...
|
|
34
|
+
async def get_device_info(self, context: Optional[Dict[str, Any]] = None) -> DeviceInfoResponse: ...
|
|
35
35
|
async def set_device_name(self, device_name: str) -> DeviceNameResponse: ...
|
|
36
36
|
|
|
37
37
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import sqlite3
|
|
3
4
|
from typing import Any, Dict, Optional
|
|
4
5
|
|
|
6
|
+
from ..__version__ import __version__
|
|
5
7
|
from ..core.api_models import (
|
|
6
8
|
DeviceInfoResponse,
|
|
7
9
|
DeviceNameResponse,
|
|
@@ -61,7 +63,8 @@ class LocalDeviceService:
|
|
|
61
63
|
_ = (pairing_code, keep_existing_data)
|
|
62
64
|
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Pairing not implemented")
|
|
63
65
|
|
|
64
|
-
async def get_device_info(self) -> DeviceInfoResponse:
|
|
66
|
+
async def get_device_info(self, context: Optional[Dict[str, Any]] = None) -> DeviceInfoResponse:
|
|
67
|
+
_ = context
|
|
65
68
|
# Get user_id from database (set by connection_info handler) or fall back to settings
|
|
66
69
|
user_id = None
|
|
67
70
|
if state.db_conn:
|
|
@@ -92,8 +95,9 @@ class LocalDeviceService:
|
|
|
92
95
|
engine_mode=state.get_engine_mode(),
|
|
93
96
|
llm_enabled=settings.enable_llm and state.get_engine_mode() == "full",
|
|
94
97
|
database_mode=settings.topos_database_mode,
|
|
98
|
+
database_version=sqlite3.sqlite_version if settings.topos_database_mode in {"local", "sqlite"} else None,
|
|
95
99
|
engine_name=device_name,
|
|
96
|
-
engine_version=
|
|
100
|
+
engine_version=__version__,
|
|
97
101
|
system=state.get_system_info(),
|
|
98
102
|
last_sync_at=last_sync_at,
|
|
99
103
|
last_received_hlc_ts=last_received_hlc_ts,
|
|
@@ -7,7 +7,9 @@ from uuid import uuid4
|
|
|
7
7
|
|
|
8
8
|
from fastapi import HTTPException, status
|
|
9
9
|
|
|
10
|
+
from ..__version__ import __version__
|
|
10
11
|
from ..config.settings import settings
|
|
12
|
+
from ..core import state
|
|
11
13
|
from ..core.api_models import (
|
|
12
14
|
DeviceInfoResponse,
|
|
13
15
|
DeviceNameResponse,
|
|
@@ -22,6 +24,7 @@ from ..storage.db.postgres import (
|
|
|
22
24
|
connect_postgres,
|
|
23
25
|
execute_query,
|
|
24
26
|
fetch_all,
|
|
27
|
+
fetch_one,
|
|
25
28
|
)
|
|
26
29
|
|
|
27
30
|
|
|
@@ -364,20 +367,80 @@ class PostgresDbService:
|
|
|
364
367
|
}
|
|
365
368
|
|
|
366
369
|
|
|
370
|
+
def _hosted_postgres_version() -> Optional[str]:
|
|
371
|
+
try:
|
|
372
|
+
with connect_postgres() as conn:
|
|
373
|
+
row = fetch_one(conn, "SELECT version() AS version", ())
|
|
374
|
+
if row and row[0]:
|
|
375
|
+
return str(row[0]).strip() or None
|
|
376
|
+
except Exception:
|
|
377
|
+
return None
|
|
378
|
+
return None
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def _hosted_dataset_id(context: Optional[Dict[str, Any]], user_id: Optional[str]) -> Optional[str]:
|
|
382
|
+
ctx = context if isinstance(context, dict) else {}
|
|
383
|
+
explicit = str(ctx.get("dataset_id") or "").strip()
|
|
384
|
+
if explicit:
|
|
385
|
+
return explicit
|
|
386
|
+
if not user_id:
|
|
387
|
+
return None
|
|
388
|
+
tenant_id = str(ctx.get("tenant_id") or "").strip()
|
|
389
|
+
if tenant_id:
|
|
390
|
+
return f"{user_id}:default:{tenant_id}"
|
|
391
|
+
default_dataset = (settings.topos_default_dataset_id or "default").strip() or "default"
|
|
392
|
+
return f"{user_id}:{default_dataset}"
|
|
393
|
+
|
|
394
|
+
|
|
367
395
|
class HostedDeviceService:
|
|
368
396
|
async def get_pairing_code(self) -> PairingCodeResponse:
|
|
369
|
-
raise
|
|
397
|
+
raise HTTPException(
|
|
398
|
+
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
399
|
+
detail="Pairing is not available for hosted runtimes",
|
|
400
|
+
)
|
|
370
401
|
|
|
371
402
|
async def pair_device(self, pairing_code: str, keep_existing_data: bool) -> PairDeviceResponse:
|
|
372
403
|
_ = (pairing_code, keep_existing_data)
|
|
373
|
-
raise
|
|
404
|
+
raise HTTPException(
|
|
405
|
+
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
406
|
+
detail="Pairing is not available for hosted runtimes",
|
|
407
|
+
)
|
|
374
408
|
|
|
375
|
-
async def get_device_info(self) -> DeviceInfoResponse:
|
|
376
|
-
|
|
409
|
+
async def get_device_info(self, context: Optional[Dict[str, Any]] = None) -> DeviceInfoResponse:
|
|
410
|
+
ctx = context if isinstance(context, dict) else {}
|
|
411
|
+
user_id = str(ctx.get("owner_user_id") or settings.topos_user_id or "").strip() or None
|
|
412
|
+
dataset_id = _hosted_dataset_id(ctx, user_id)
|
|
413
|
+
engine_mode = state.get_engine_mode()
|
|
414
|
+
device_name = (settings.engine_name or "").strip() or None
|
|
415
|
+
|
|
416
|
+
return DeviceInfoResponse(
|
|
417
|
+
user_id=user_id,
|
|
418
|
+
dataset_id=dataset_id,
|
|
419
|
+
sync_connected=False,
|
|
420
|
+
sync_enabled=False,
|
|
421
|
+
engine_class=state.get_engine_class(),
|
|
422
|
+
engine_mode=engine_mode,
|
|
423
|
+
llm_enabled=bool(settings.enable_llm and engine_mode == "full"),
|
|
424
|
+
database_mode=settings.topos_database_mode or "postgres",
|
|
425
|
+
database_version=_hosted_postgres_version(),
|
|
426
|
+
engine_name=device_name,
|
|
427
|
+
engine_version=__version__,
|
|
428
|
+
system={},
|
|
429
|
+
last_sync_at=None,
|
|
430
|
+
last_received_hlc_ts=None,
|
|
431
|
+
last_received_op_id=None,
|
|
432
|
+
oplog_count=None,
|
|
433
|
+
oplog_bytes=None,
|
|
434
|
+
ops_since_last_sync=None,
|
|
435
|
+
oplog_bytes_since_last_sync=None,
|
|
436
|
+
)
|
|
377
437
|
|
|
378
438
|
async def set_device_name(self, device_name: str) -> DeviceNameResponse:
|
|
379
439
|
_ = device_name
|
|
380
|
-
raise
|
|
440
|
+
raise HTTPException(
|
|
441
|
+
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
442
|
+
detail="Device rename is not available for hosted runtimes",
|
|
443
|
+
)
|
|
381
444
|
|
|
382
445
|
|
|
383
446
|
class HostedSyncService:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|