typedef-data-intelligence 0.35.0__tar.gz → 0.37.0__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.
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/CHANGELOG.md +19 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/PKG-INFO +1 -1
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/pyproject.toml +1 -1
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/app.py +69 -15
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_quickstart.py +10 -4
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_semdiff_provision.py +13 -8
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_sync.py +11 -4
- typedef_data_intelligence-0.37.0/src/lineage/observability/__init__.py +1 -0
- typedef_data_intelligence-0.37.0/src/lineage/observability/desktop_logfire.py +97 -0
- typedef_data_intelligence-0.37.0/src/lineage/observability/local_otlp_export.py +109 -0
- typedef_data_intelligence-0.37.0/src/lineage/observability/local_span_export.py +93 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/typedef_cli.py +10 -4
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/git.py +23 -5
- typedef_data_intelligence-0.37.0/tests/api/test_app_logfire.py +279 -0
- typedef_data_intelligence-0.37.0/tests/api/test_app_retention_wiring.py +20 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_diagnostics.py +202 -3
- typedef_data_intelligence-0.37.0/tests/api/test_local_otlp_export.py +72 -0
- typedef_data_intelligence-0.37.0/tests/observability/test_no_raw_stream_filehandler.py +36 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_git_utils.py +17 -5
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/.dockerignore +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/.env.example +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/.gitignore +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/.justfile_help.txt +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/Dockerfile +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/Dockerfile.dev +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/Justfile +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/README.pypi.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/config.api.yml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/config.cli.yml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/config.desktop.yml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/config.ingest.unified.yml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/config.ingest.yml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docker-entrypoint.dev.sh +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/LINEAR_TICKETING_BACKEND.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/BUSINESS_CONCEPT_INDEX.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/CLUSTERING_ALGORITHM_DECISION.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/GENERIC_REPORT_SCHEMA.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/HYBRID_ANALYSIS_ARCHITECTURE.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/PHASE2_IR_STITCHING.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/PIPELINE_PRIMITIVES_USECASES.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/SALESFORCE_REPORT_IR.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/SEMANTIC_JOIN_RESOLUTION.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/SEMANTIC_SCHEMA_DESIGN.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/AGENT_DESIGN_NOTES.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/COLUMN_PROFILING.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/COLUMN_PROFILING_IMPLEMENTATION.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/EXTRACTION_DETECTION.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/PERFORMANCE_AND_QUALITY_SPECIALISTS.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/STREAMING_SUBAGENT_ARCHITECTURE.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/examples/memory_usage_examples.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/examples/pydantic_tool_customization_example.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/AGENT_USER_GUIDE.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/GETTING_STARTED.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/MIGRATION_THREE_SQL.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/SCOPE_LEVEL_LINEAGE_GUIDE.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/SEMANTIC_DIFF_HARNESS.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/SEMANTIC_DIMENSIONS_GUIDE.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/TYPE_ENUMS.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/pytest.ini +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/create_salesforce_reports.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/data_generation/utils.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/databricks-smoke.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/delete_salesforce_reports.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/populate_salesforce_schema.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/reset_demo_git_workspace.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/reset_demo_linear.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/snowflake_server.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/surreal-engine-bench.sh +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/surreal-fts-b2-corpus.sh +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/surreal-fts-bench.sh +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/surreal-fts-quality.sh +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/snowflake-mcp-config.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/auth_context.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/concept_cards.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/controlplane_identity.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_agents.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_context.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_logs.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_runtime.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_setup.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_wizard_sync.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_workspaces.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_worktree.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/graph_availability.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/posthog_analytics.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/semantic_runtime.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/serve.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/context_projects/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/context_projects/airflow_discovery.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/context_projects/dbt_plugin.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/backward_dryrun.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/clustering_metrics.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/drop_snowflake_tables.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/ephemeral_lineage_report.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/eval_clustering.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/list_ephemeral_models.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/refingerprint_cache.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/regen_semantic_goldens.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/reset_git_workspace.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/reset_linear_workspace.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/reset_project.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/run_dbt_workflow.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/seed_census.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/semantic_goldens.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_bench.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_cells.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_corpus.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_corpus_dump.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_coverage.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_coverage_report.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_oracle.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_quality.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_quality_bench.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/log_parse.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/report.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/runner.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/README.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/cli.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/db_names.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/dbt_compile.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/diff_runner.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/expected_changes.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/column_rename_downstream/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/column_rename_downstream/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_coarsen/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_coarsen/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_deleted_leaf_model/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_deleted_leaf_model/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_fanout_compound_oli/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_fanout_compound_oli/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_gold_control/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_gold_control/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_v1_003_user_telemetry/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_v1_003_user_telemetry/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures_loader.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/ingest.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/lifecycle.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/source_prep.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/surreal/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/surreal/desktop_mcp.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/unified_cli.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/mcp/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/mcp/runner.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/mcp/server.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/templates/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/templates/typedef_config.yaml.j2 +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/databricks.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/databricks_oauth.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/dbt.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/dbt_profile.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/env.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/gitignore.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/logging.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/snowflake.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_app_lifespan.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_concept_cards.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_databricks_auth_wiring.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_agents_clone_backend.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_agents_runtime.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_agents_ticket_resolution.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_airflow_snapshot.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_clerk_auth.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_context_reader_cache.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_databases_delete.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_databases_edit.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_databricks_oauth.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_databricks_setup.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_delete_endpoints.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_logs.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_orphan_flags.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_posthog.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_quickstart.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_repo_projects_discovery.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_runtime.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_salesforce.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_save_env_credentials.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_search.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_secondary_roles_hook.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_semdiff_provision.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_setup.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_setup_clone.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_setup_env_vars.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_sync.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_sync_render_profile.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_telemetry.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_adopt.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_context_kinds.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_delete.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_gold_triggers.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_integrations.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_preview_name.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_projects_and_repos.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_r3_r10.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_summary.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_target_scope.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_wizard.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree_clone.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree_clone_token_scope.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree_gold_queue.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree_kind_target.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree_resolution_scope.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_graph_availability_any_sentinel.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_no_legacy_surface.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_surreal_graph_availability.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_target_envelope_reads.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_validate_project_collisions.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_wizard_create_project_integration_persistence.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_workspace_clone_settings.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_worktree_registry_workspaces_shape.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/backends/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/backends/lineage/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/backends/lineage/test_search_enum_coverage.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/backends/lineage/test_surreal_search.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/conftest.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/test_fts_cells.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/test_fts_coverage.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/test_fts_quality.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/test_log_parse.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/test_report.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/test_typedef_home_provision.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/test_worktree_reclaim.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/docs/test_desktop_posthog_event_docs.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/integration/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/integration/test_semantic_diff_harness.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/mcp/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/mcp/test_desktop_mcp_retry.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/mcp/test_list_projects.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/mcp/test_server.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_gitignore_utils.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_helpers_semantic.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_model_tier_validation.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_template_yaml_escaping.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_typedef_cli_import_budget.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_unified_cli_airflow.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_unified_cli_fast.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_unified_cli_semantic.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/context_projects/test_airflow_discovery.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/context_projects/test_dbt_plugin.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/test_semdiff_expected_changes.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/test_semdiff_harness_units.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/test_semdiff_lifecycle.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/test_semdiff_lifecycle_io.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/test_semdiff_lifecycle_provisioning.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/formula/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_databricks_utils.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_dbt_docs_generate.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_dbt_profile.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_logging.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_run_dbt_command.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_snowflake_utils.py +0 -0
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.37.0](https://github.com/typedef-ai/typedef/compare/typedef-data-intelligence-v0.36.0...typedef-data-intelligence-v0.37.0) (2026-07-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **observability:** retention & rotation policy for local logs/traces (TD-3813) ([#1123](https://github.com/typedef-ai/typedef/issues/1123)) ([48dc274](https://github.com/typedef-ai/typedef/commit/48dc274d2e8182a0a2417abf3ba4028278cf8939))
|
|
9
|
+
|
|
10
|
+
## [0.36.0](https://github.com/typedef-ai/typedef/compare/typedef-data-intelligence-v0.35.0...typedef-data-intelligence-v0.36.0) (2026-07-07)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **observability:** enable local desktop span export (TD-3805) ([#1114](https://github.com/typedef-ai/typedef/issues/1114)) ([32cf196](https://github.com/typedef-ai/typedef/commit/32cf196272f658a639ef3d6ae9ce3599c79078eb))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **observability:** classify + clean customer data in retained/exported logs (TD-3825) ([#1107](https://github.com/typedef-ai/typedef/issues/1107)) ([cac553d](https://github.com/typedef-ai/typedef/commit/cac553d50a84275a734d198baca25a7290c6ab70))
|
|
21
|
+
|
|
3
22
|
## [0.35.0](https://github.com/typedef-ai/typedef/compare/typedef-data-intelligence-v0.34.0...typedef-data-intelligence-v0.35.0) (2026-07-06)
|
|
4
23
|
|
|
5
24
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: typedef-data-intelligence
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.37.0
|
|
4
4
|
Summary: Unified lineage management and AI-powered analytics platform for dbt projects
|
|
5
5
|
Project-URL: Homepage, https://typedef.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/typedef-ai/data-intelligence
|
{typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/app.py
RENAMED
|
@@ -6,9 +6,8 @@ import sys
|
|
|
6
6
|
from contextlib import asynccontextmanager
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Any, Optional
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
10
10
|
|
|
11
|
-
import logfire
|
|
12
11
|
import yaml
|
|
13
12
|
from agent.api import shutdown_event
|
|
14
13
|
from agent.api.profiles import (
|
|
@@ -37,6 +36,10 @@ from core.backends.unified_graph.surreal.reader import SurrealUnifiedGraphReader
|
|
|
37
36
|
from fastapi import FastAPI, HTTPException, Request
|
|
38
37
|
from fastapi.middleware.cors import CORSMiddleware
|
|
39
38
|
from lineage.api.auth_context import resolve_user_context
|
|
39
|
+
from lineage.observability.desktop_logfire import (
|
|
40
|
+
configure_desktop_logfire,
|
|
41
|
+
shutdown_desktop_logfire,
|
|
42
|
+
)
|
|
40
43
|
from lineage.utils.env import load_env_file
|
|
41
44
|
from pydantic import ValidationError
|
|
42
45
|
|
|
@@ -250,6 +253,34 @@ class AppState:
|
|
|
250
253
|
unified_reader: Optional[SurrealUnifiedGraphReader] = None
|
|
251
254
|
|
|
252
255
|
|
|
256
|
+
# ============================================================================
|
|
257
|
+
# Observability Retention (TD-3813)
|
|
258
|
+
# ============================================================================
|
|
259
|
+
|
|
260
|
+
if TYPE_CHECKING:
|
|
261
|
+
from core.observability.retention import RetentionPolicy
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
async def _periodic_retention_sweep(policy: "RetentionPolicy") -> None:
|
|
265
|
+
"""Run the observability retention sweep periodically, off the event loop.
|
|
266
|
+
|
|
267
|
+
Sleeps ``policy.periodic_interval_seconds`` between sweeps. Best-effort;
|
|
268
|
+
only cancellation stops it.
|
|
269
|
+
"""
|
|
270
|
+
import asyncio
|
|
271
|
+
|
|
272
|
+
from core.observability.retention import sweep_observability
|
|
273
|
+
|
|
274
|
+
while True:
|
|
275
|
+
try:
|
|
276
|
+
await asyncio.sleep(policy.periodic_interval_seconds)
|
|
277
|
+
await asyncio.to_thread(sweep_observability, policy)
|
|
278
|
+
except asyncio.CancelledError:
|
|
279
|
+
raise
|
|
280
|
+
except Exception:
|
|
281
|
+
logger.debug("periodic observability retention sweep failed", exc_info=True)
|
|
282
|
+
|
|
283
|
+
|
|
253
284
|
# ============================================================================
|
|
254
285
|
# Middleware for Request Logging and User Context Extraction
|
|
255
286
|
# ============================================================================
|
|
@@ -305,10 +336,7 @@ async def lifespan(app: FastAPI):
|
|
|
305
336
|
logger.debug("gold-build temp dir cleanup failed", exc_info=True)
|
|
306
337
|
|
|
307
338
|
try:
|
|
308
|
-
from core.paths import
|
|
309
|
-
cleanup_stale_frontend_logs,
|
|
310
|
-
cleanup_stale_wizard_scratch,
|
|
311
|
-
)
|
|
339
|
+
from core.paths import cleanup_stale_wizard_scratch
|
|
312
340
|
|
|
313
341
|
removed_wizard_scratch = cleanup_stale_wizard_scratch()
|
|
314
342
|
if removed_wizard_scratch:
|
|
@@ -317,13 +345,25 @@ async def lifespan(app: FastAPI):
|
|
|
317
345
|
len(removed_wizard_scratch),
|
|
318
346
|
)
|
|
319
347
|
|
|
320
|
-
# TD-
|
|
321
|
-
#
|
|
322
|
-
|
|
323
|
-
|
|
348
|
+
# TD-3813: one unified age+size retention sweep over the whole logs/
|
|
349
|
+
# tree (traces, index, ops, streams tails, frontend), then a periodic
|
|
350
|
+
# task so an always-on session can't grow unbounded between restarts.
|
|
351
|
+
import asyncio
|
|
352
|
+
|
|
353
|
+
from core.observability.retention import RetentionPolicy, sweep_observability
|
|
354
|
+
|
|
355
|
+
_obs_policy = RetentionPolicy.from_env()
|
|
356
|
+
swept = sweep_observability(_obs_policy)
|
|
357
|
+
_swept_total = sum(len(v) for v in swept.values())
|
|
358
|
+
if _swept_total:
|
|
324
359
|
logger.info(
|
|
325
|
-
"
|
|
326
|
-
|
|
360
|
+
"Observability retention: removed %d unit(s) on startup (%s)",
|
|
361
|
+
_swept_total,
|
|
362
|
+
", ".join(f"{k}={len(v)}" for k, v in swept.items() if v),
|
|
363
|
+
)
|
|
364
|
+
if not _obs_policy.disabled:
|
|
365
|
+
app.state._obs_retention_task = asyncio.create_task(
|
|
366
|
+
_periodic_retention_sweep(_obs_policy)
|
|
327
367
|
)
|
|
328
368
|
|
|
329
369
|
try:
|
|
@@ -396,9 +436,9 @@ async def lifespan(app: FastAPI):
|
|
|
396
436
|
# If user opts out via the consent modal or Settings, takes effect on next launch.
|
|
397
437
|
from lineage.api.desktop_setup import _read_telemetry_prefs
|
|
398
438
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
439
|
+
configure_desktop_logfire(
|
|
440
|
+
telemetry_opted_out=bool(_read_telemetry_prefs().get("telemetry_opted_out"))
|
|
441
|
+
)
|
|
402
442
|
|
|
403
443
|
# Startup: no maintained API route initializes the legacy state path.
|
|
404
444
|
if _NEEDS_LEGACY_STATE:
|
|
@@ -746,6 +786,19 @@ async def lifespan(app: FastAPI):
|
|
|
746
786
|
"Failed to terminate active gold workers", exc_info=True
|
|
747
787
|
)
|
|
748
788
|
|
|
789
|
+
_obs_task = getattr(app.state, "_obs_retention_task", None)
|
|
790
|
+
if _obs_task is not None:
|
|
791
|
+
import asyncio
|
|
792
|
+
|
|
793
|
+
_obs_task.cancel()
|
|
794
|
+
try:
|
|
795
|
+
await _obs_task
|
|
796
|
+
except (asyncio.CancelledError, Exception):
|
|
797
|
+
# await of a just-cancelled task re-raises CancelledError (a BaseException,
|
|
798
|
+
# NOT Exception in 3.12), so both must be caught; do not simplify to bare
|
|
799
|
+
# except Exception.
|
|
800
|
+
pass
|
|
801
|
+
|
|
749
802
|
try:
|
|
750
803
|
from core.observability.manifest import finalize_open_ops
|
|
751
804
|
|
|
@@ -759,6 +812,7 @@ async def lifespan(app: FastAPI):
|
|
|
759
812
|
shutdown_shared_posthog_client()
|
|
760
813
|
except Exception:
|
|
761
814
|
logger.debug("Failed to shut down PostHog analytics client", exc_info=True)
|
|
815
|
+
shutdown_desktop_logfire()
|
|
762
816
|
|
|
763
817
|
# Close standalone data backend (e.g. DuckDB file handle)
|
|
764
818
|
standalone_db = getattr(app.state, "standalone_data_backend", None)
|
|
@@ -743,13 +743,19 @@ def _quickstart_worker(
|
|
|
743
743
|
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
744
744
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
745
745
|
)
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
746
|
+
from core.observability.retention import (
|
|
747
|
+
RetentionPolicy,
|
|
748
|
+
attach_rotating_stream_handler,
|
|
749
|
+
)
|
|
749
750
|
|
|
750
751
|
root = _logging.getLogger()
|
|
751
752
|
root.setLevel(_logging.INFO)
|
|
752
|
-
|
|
753
|
+
attach_rotating_stream_handler(
|
|
754
|
+
root,
|
|
755
|
+
_log_file,
|
|
756
|
+
max_head_bytes=RetentionPolicy.from_env().stream_head_max_bytes,
|
|
757
|
+
formatter=_fmt,
|
|
758
|
+
)
|
|
753
759
|
from lineage.utils.logging import quiet_sync_dependency_loggers
|
|
754
760
|
|
|
755
761
|
quiet_sync_dependency_loggers()
|
|
@@ -78,17 +78,22 @@ def _attach_diff_log_handler() -> None:
|
|
|
78
78
|
(``type_inference.*``, ``ingest.*``, ``core.backends.unified_graph.*``)
|
|
79
79
|
propagates to the child's root and lands in ``diff.log``.
|
|
80
80
|
"""
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
logging.Formatter(
|
|
85
|
-
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
86
|
-
datefmt="%Y-%m-%d %H:%M:%S",
|
|
87
|
-
)
|
|
81
|
+
from core.observability.retention import (
|
|
82
|
+
RetentionPolicy,
|
|
83
|
+
attach_rotating_stream_handler,
|
|
88
84
|
)
|
|
85
|
+
|
|
89
86
|
root = logging.getLogger()
|
|
90
87
|
root.setLevel(logging.INFO)
|
|
91
|
-
|
|
88
|
+
attach_rotating_stream_handler(
|
|
89
|
+
root,
|
|
90
|
+
_diff_log_path(),
|
|
91
|
+
max_head_bytes=RetentionPolicy.from_env().stream_head_max_bytes,
|
|
92
|
+
formatter=logging.Formatter(
|
|
93
|
+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
94
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
95
|
+
),
|
|
96
|
+
)
|
|
92
97
|
|
|
93
98
|
|
|
94
99
|
def _load_env() -> None:
|
|
@@ -2873,12 +2873,19 @@ def _sync_worker(
|
|
|
2873
2873
|
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
2874
2874
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
2875
2875
|
)
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2876
|
+
from core.observability.retention import (
|
|
2877
|
+
RetentionPolicy,
|
|
2878
|
+
attach_rotating_stream_handler,
|
|
2879
|
+
)
|
|
2880
|
+
|
|
2879
2881
|
root = _logging.getLogger()
|
|
2880
2882
|
root.setLevel(_logging.INFO)
|
|
2881
|
-
|
|
2883
|
+
attach_rotating_stream_handler(
|
|
2884
|
+
root,
|
|
2885
|
+
_logs / "sync.log",
|
|
2886
|
+
max_head_bytes=RetentionPolicy.from_env().stream_head_max_bytes,
|
|
2887
|
+
formatter=_fmt,
|
|
2888
|
+
)
|
|
2882
2889
|
|
|
2883
2890
|
from lineage.utils.logging import quiet_sync_dependency_loggers
|
|
2884
2891
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Local observability helpers for the desktop backend."""
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Desktop Logfire setup with local trace export processors."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
from typing import Final
|
|
8
|
+
|
|
9
|
+
import logfire
|
|
10
|
+
from opentelemetry.sdk.trace import SpanProcessor
|
|
11
|
+
|
|
12
|
+
from lineage.observability.local_otlp_export import make_local_otlp_span_processor
|
|
13
|
+
from lineage.observability.local_span_export import make_local_span_processor
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
_OTEL_EXPORTER_ENV_KEYS: Final = (
|
|
18
|
+
"OTEL_TRACES_EXPORTER",
|
|
19
|
+
"OTEL_METRICS_EXPORTER",
|
|
20
|
+
"OTEL_LOGS_EXPORTER",
|
|
21
|
+
)
|
|
22
|
+
_OTEL_REMOTE_EXPORT_ENV_KEYS: Final = (
|
|
23
|
+
*_OTEL_EXPORTER_ENV_KEYS,
|
|
24
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
25
|
+
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
|
|
26
|
+
"OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
|
|
27
|
+
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
|
|
28
|
+
)
|
|
29
|
+
_local_only_previous_otel_exporters: dict[str, str | None] | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def configure_desktop_logfire(*, telemetry_opted_out: bool) -> None:
|
|
33
|
+
"""Configure cloud-gated Logfire and local desktop span processors."""
|
|
34
|
+
logfire_token = os.getenv("LOGFIRE_TOKEN") or None
|
|
35
|
+
span_processors = _desktop_span_processors()
|
|
36
|
+
send_to_logfire = logfire_token is not None and not telemetry_opted_out
|
|
37
|
+
if send_to_logfire:
|
|
38
|
+
logfire.configure(
|
|
39
|
+
token=logfire_token,
|
|
40
|
+
send_to_logfire=True,
|
|
41
|
+
console=False,
|
|
42
|
+
additional_span_processors=span_processors,
|
|
43
|
+
)
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
_disable_otel_exporters_for_local_only()
|
|
47
|
+
logfire.configure(
|
|
48
|
+
token=None,
|
|
49
|
+
send_to_logfire=False,
|
|
50
|
+
metrics=False,
|
|
51
|
+
console=False,
|
|
52
|
+
additional_span_processors=span_processors,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def shutdown_desktop_logfire() -> None:
|
|
57
|
+
"""Flush configured Logfire processors and restore local-only OTEL env changes."""
|
|
58
|
+
try:
|
|
59
|
+
logfire.shutdown()
|
|
60
|
+
except Exception:
|
|
61
|
+
logger.debug("Failed to shut down Logfire", exc_info=True)
|
|
62
|
+
finally:
|
|
63
|
+
_restore_local_only_otel_exporters()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _desktop_span_processors() -> list[SpanProcessor]:
|
|
67
|
+
processors: list[SpanProcessor] = [make_local_span_processor()]
|
|
68
|
+
local_otlp_processor = make_local_otlp_span_processor()
|
|
69
|
+
if local_otlp_processor is not None:
|
|
70
|
+
processors.append(local_otlp_processor)
|
|
71
|
+
return processors
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _disable_otel_exporters_for_local_only() -> None:
|
|
75
|
+
global _local_only_previous_otel_exporters
|
|
76
|
+
if _local_only_previous_otel_exporters is not None:
|
|
77
|
+
_restore_local_only_otel_exporters()
|
|
78
|
+
if not any(os.getenv(key) for key in _OTEL_REMOTE_EXPORT_ENV_KEYS):
|
|
79
|
+
return
|
|
80
|
+
_local_only_previous_otel_exporters = {
|
|
81
|
+
key: os.environ.get(key) for key in _OTEL_EXPORTER_ENV_KEYS
|
|
82
|
+
}
|
|
83
|
+
for key in _OTEL_EXPORTER_ENV_KEYS:
|
|
84
|
+
os.environ[key] = "none"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _restore_local_only_otel_exporters() -> None:
|
|
88
|
+
global _local_only_previous_otel_exporters
|
|
89
|
+
previous_exporters = _local_only_previous_otel_exporters
|
|
90
|
+
if previous_exporters is None:
|
|
91
|
+
return
|
|
92
|
+
for key, value in previous_exporters.items():
|
|
93
|
+
if value is None:
|
|
94
|
+
os.environ.pop(key, None)
|
|
95
|
+
else:
|
|
96
|
+
os.environ[key] = value
|
|
97
|
+
_local_only_previous_otel_exporters = None
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Local OTLP/HTTP export for explicitly enabled desktop development traces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ipaddress
|
|
6
|
+
import os
|
|
7
|
+
from typing import Final
|
|
8
|
+
from urllib.parse import SplitResult, urlsplit, urlunsplit
|
|
9
|
+
|
|
10
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
11
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
12
|
+
|
|
13
|
+
_LOCAL_OTLP_EXPORT_ENV: Final = "TYPEDEF_ENABLE_LOCAL_OTLP_EXPORT"
|
|
14
|
+
_GENERIC_ENDPOINT_ENV: Final = "OTEL_EXPORTER_OTLP_ENDPOINT"
|
|
15
|
+
_TRACES_ENDPOINT_ENV: Final = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"
|
|
16
|
+
_GENERIC_PROTOCOL_ENV: Final = "OTEL_EXPORTER_OTLP_PROTOCOL"
|
|
17
|
+
_TRACES_PROTOCOL_ENV: Final = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"
|
|
18
|
+
_DEFAULT_TRACES_ENDPOINT: Final = "http://127.0.0.1:4318/v1/traces"
|
|
19
|
+
_TRACES_PATH: Final = "/v1/traces"
|
|
20
|
+
_HTTP_PROTOBUF_PROTOCOL: Final = "http/protobuf"
|
|
21
|
+
_ENABLED_VALUES: Final = frozenset({"1", "true", "yes", "on"})
|
|
22
|
+
_LOOPBACK_HOSTNAMES: Final = frozenset({"localhost"})
|
|
23
|
+
_EXPORT_TIMEOUT_SECONDS: Final = 2.0
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def resolve_local_otlp_traces_endpoint() -> str | None:
|
|
27
|
+
"""Resolve a loopback OTLP/HTTP traces endpoint from development env config."""
|
|
28
|
+
if not _local_otlp_export_enabled():
|
|
29
|
+
return None
|
|
30
|
+
if _resolved_protocol() != _HTTP_PROTOBUF_PROTOCOL:
|
|
31
|
+
return None
|
|
32
|
+
traces_endpoint = _clean_env(_TRACES_ENDPOINT_ENV)
|
|
33
|
+
if traces_endpoint is not None:
|
|
34
|
+
return _validated_loopback_endpoint(traces_endpoint)
|
|
35
|
+
generic_endpoint = _clean_env(_GENERIC_ENDPOINT_ENV)
|
|
36
|
+
if generic_endpoint is None:
|
|
37
|
+
return _DEFAULT_TRACES_ENDPOINT
|
|
38
|
+
return _validated_loopback_endpoint(_with_traces_path(generic_endpoint))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def make_local_otlp_span_processor() -> BatchSpanProcessor | None:
|
|
42
|
+
"""Create a local OTLP/HTTP span processor when development export is enabled."""
|
|
43
|
+
endpoint = resolve_local_otlp_traces_endpoint()
|
|
44
|
+
if endpoint is None:
|
|
45
|
+
return None
|
|
46
|
+
return BatchSpanProcessor(
|
|
47
|
+
OTLPSpanExporter(endpoint=endpoint, timeout=_EXPORT_TIMEOUT_SECONDS)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _local_otlp_export_enabled() -> bool:
|
|
52
|
+
value = _clean_env(_LOCAL_OTLP_EXPORT_ENV)
|
|
53
|
+
return value is not None and value.lower() in _ENABLED_VALUES
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _resolved_protocol() -> str:
|
|
57
|
+
traces_protocol = _clean_env(_TRACES_PROTOCOL_ENV)
|
|
58
|
+
if traces_protocol is not None:
|
|
59
|
+
return traces_protocol.lower()
|
|
60
|
+
generic_protocol = _clean_env(_GENERIC_PROTOCOL_ENV)
|
|
61
|
+
if generic_protocol is not None:
|
|
62
|
+
return generic_protocol.lower()
|
|
63
|
+
return _HTTP_PROTOBUF_PROTOCOL
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _clean_env(key: str) -> str | None:
|
|
67
|
+
value = os.getenv(key)
|
|
68
|
+
if value is None:
|
|
69
|
+
return None
|
|
70
|
+
clean_value = value.strip()
|
|
71
|
+
if clean_value:
|
|
72
|
+
return clean_value
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _with_traces_path(endpoint: str) -> str:
|
|
77
|
+
parsed = urlsplit(endpoint)
|
|
78
|
+
clean_path = parsed.path.rstrip("/")
|
|
79
|
+
if clean_path.endswith(_TRACES_PATH):
|
|
80
|
+
return endpoint
|
|
81
|
+
traces_path = f"{clean_path}{_TRACES_PATH}" if clean_path else _TRACES_PATH
|
|
82
|
+
return urlunsplit(
|
|
83
|
+
SplitResult(
|
|
84
|
+
scheme=parsed.scheme,
|
|
85
|
+
netloc=parsed.netloc,
|
|
86
|
+
path=traces_path,
|
|
87
|
+
query=parsed.query,
|
|
88
|
+
fragment=parsed.fragment,
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _validated_loopback_endpoint(endpoint: str) -> str | None:
|
|
94
|
+
parsed = urlsplit(endpoint)
|
|
95
|
+
if parsed.scheme not in {"http", "https"}:
|
|
96
|
+
return None
|
|
97
|
+
if parsed.username is not None or parsed.password is not None:
|
|
98
|
+
return None
|
|
99
|
+
if parsed.query or parsed.fragment:
|
|
100
|
+
return None
|
|
101
|
+
hostname = parsed.hostname
|
|
102
|
+
if hostname is None:
|
|
103
|
+
return None
|
|
104
|
+
if hostname.lower() in _LOOPBACK_HOSTNAMES:
|
|
105
|
+
return endpoint
|
|
106
|
+
try:
|
|
107
|
+
return endpoint if ipaddress.ip_address(hostname).is_loopback else None
|
|
108
|
+
except ValueError:
|
|
109
|
+
return None
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""OpenTelemetry span export for local desktop traces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
from collections.abc import Sequence
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Final
|
|
11
|
+
|
|
12
|
+
from core.paths import logs_dir
|
|
13
|
+
from opentelemetry.sdk.trace.export import (
|
|
14
|
+
BatchSpanProcessor,
|
|
15
|
+
ReadableSpan,
|
|
16
|
+
SpanExporter,
|
|
17
|
+
SpanExportResult,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
_TRACE_FILE_PREFIX: Final = "spans"
|
|
21
|
+
_DEFAULT_TRACE_CHUNK_BYTES: Final = 64 * 1024 * 1024
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class NdjsonFileSpanExporter(SpanExporter):
|
|
25
|
+
"""Write OpenTelemetry spans to per-process NDJSON chunks, rolling to a new chunk.
|
|
26
|
+
|
|
27
|
+
Chunks rotate when the current one crosses ``max_file_bytes`` so a long-lived
|
|
28
|
+
process (the backend) never grows one unbounded file (TD-3813 blocker #2).
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self, traces_dir: Path, max_file_bytes: int = _DEFAULT_TRACE_CHUNK_BYTES
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Create an exporter rooted at the configured traces directory."""
|
|
35
|
+
self._traces_dir = traces_dir
|
|
36
|
+
self._pid = os.getpid()
|
|
37
|
+
# per-instance uniqueness key: (pid, start_ns, seq) names each chunk; one
|
|
38
|
+
# exporter per process, so start_ns never collides across restarts.
|
|
39
|
+
self._start_ns = time.time_ns()
|
|
40
|
+
self._seq = 0
|
|
41
|
+
self._max_file_bytes = max_file_bytes
|
|
42
|
+
self._path = self._chunk_path()
|
|
43
|
+
self._lock = threading.Lock()
|
|
44
|
+
self._closed = False
|
|
45
|
+
|
|
46
|
+
def _chunk_path(self) -> Path:
|
|
47
|
+
return (
|
|
48
|
+
self._traces_dir
|
|
49
|
+
/ f"{_TRACE_FILE_PREFIX}-{self._pid}-{self._start_ns}-{self._seq:04d}.ndjson"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def _maybe_rotate(self) -> None:
|
|
53
|
+
try:
|
|
54
|
+
if self._path.exists() and self._path.stat().st_size >= self._max_file_bytes:
|
|
55
|
+
self._seq += 1
|
|
56
|
+
self._path = self._chunk_path()
|
|
57
|
+
except OSError:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
|
61
|
+
"""Append spans as one JSON object per line, rolling chunks by size."""
|
|
62
|
+
with self._lock:
|
|
63
|
+
if self._closed:
|
|
64
|
+
return SpanExportResult.FAILURE
|
|
65
|
+
try:
|
|
66
|
+
self._traces_dir.mkdir(parents=True, exist_ok=True)
|
|
67
|
+
self._maybe_rotate()
|
|
68
|
+
with self._path.open("a", encoding="utf-8") as trace_file:
|
|
69
|
+
for span in spans:
|
|
70
|
+
trace_file.write(span.to_json(indent=None))
|
|
71
|
+
trace_file.write("\n")
|
|
72
|
+
except (OSError, TypeError, ValueError):
|
|
73
|
+
return SpanExportResult.FAILURE
|
|
74
|
+
return SpanExportResult.SUCCESS
|
|
75
|
+
|
|
76
|
+
def shutdown(self) -> None:
|
|
77
|
+
"""Stop accepting new spans."""
|
|
78
|
+
with self._lock:
|
|
79
|
+
self._closed = True
|
|
80
|
+
|
|
81
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
82
|
+
"""Report that file writes are already flushed."""
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def make_local_span_processor() -> BatchSpanProcessor:
|
|
87
|
+
"""Create the always-on local span processor for desktop startup."""
|
|
88
|
+
from core.observability.retention import RetentionPolicy
|
|
89
|
+
|
|
90
|
+
chunk_bytes = RetentionPolicy.from_env().stream_head_max_bytes
|
|
91
|
+
return BatchSpanProcessor(
|
|
92
|
+
NdjsonFileSpanExporter(logs_dir() / "traces", max_file_bytes=chunk_bytes)
|
|
93
|
+
)
|
{typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/typedef_cli.py
RENAMED
|
@@ -461,13 +461,19 @@ def sync(projects: tuple[str, ...], all: bool, full: bool, verbose: bool):
|
|
|
461
461
|
log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
462
462
|
log_datefmt = "%Y-%m-%d %H:%M:%S"
|
|
463
463
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
464
|
+
from core.observability.retention import (
|
|
465
|
+
RetentionPolicy,
|
|
466
|
+
attach_rotating_stream_handler,
|
|
467
|
+
)
|
|
467
468
|
|
|
468
469
|
root_logger = logging.getLogger()
|
|
469
470
|
root_logger.setLevel(logging.INFO)
|
|
470
|
-
|
|
471
|
+
attach_rotating_stream_handler(
|
|
472
|
+
root_logger,
|
|
473
|
+
log_file,
|
|
474
|
+
max_head_bytes=RetentionPolicy.from_env().stream_head_max_bytes,
|
|
475
|
+
formatter=logging.Formatter(log_format, datefmt=log_datefmt),
|
|
476
|
+
)
|
|
471
477
|
|
|
472
478
|
if verbose:
|
|
473
479
|
console_handler = logging.StreamHandler()
|
{typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/git.py
RENAMED
|
@@ -10,6 +10,8 @@ from collections.abc import AsyncIterator
|
|
|
10
10
|
from dataclasses import dataclass
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
|
|
13
|
+
from core.models.observability import sanitize_diagnostic_text
|
|
14
|
+
|
|
13
15
|
logger = logging.getLogger(__name__)
|
|
14
16
|
|
|
15
17
|
|
|
@@ -69,7 +71,6 @@ class CloneProgress:
|
|
|
69
71
|
# Regex to parse git progress lines like:
|
|
70
72
|
# "Receiving objects: 67% (835/1247)"
|
|
71
73
|
_PROGRESS_RE = re.compile(r"(Counting|Compressing|Receiving|Resolving)[^:]*:\s*(\d+)%")
|
|
72
|
-
_URL_CREDENTIALS_RE = re.compile(r"(?P<scheme>https?://)(?:[^/\s@]+(?::[^/\s@]*)?@)")
|
|
73
74
|
|
|
74
75
|
|
|
75
76
|
def _handle_clone_stderr_line(
|
|
@@ -91,19 +92,36 @@ def _handle_clone_stderr_line(
|
|
|
91
92
|
|
|
92
93
|
|
|
93
94
|
def _build_clone_failure_message(stderr_lines: list[str], exit_code: int) -> str:
|
|
94
|
-
"""Return the
|
|
95
|
+
"""Return the clone failure text to surface to callers.
|
|
96
|
+
|
|
97
|
+
Credential shapes are stripped here (TD-3825) so every downstream consumer
|
|
98
|
+
inherits clean text: the raised ``RuntimeError``, the error ``CloneProgress``
|
|
99
|
+
event, the ``logger.debug`` full-stderr line, and anything that logs the
|
|
100
|
+
exception into ``sync.log`` / ``quickstart.log`` / ``backend-stdout.log``.
|
|
101
|
+
A token-bearing remote URL (``https://TOKEN@host/repo.git``) never reaches
|
|
102
|
+
disk from this path.
|
|
103
|
+
"""
|
|
95
104
|
stderr_detail = "\n".join(line.strip() for line in stderr_lines if line.strip())
|
|
96
|
-
|
|
105
|
+
if not stderr_detail:
|
|
106
|
+
return f"git clone failed (exit {exit_code})"
|
|
107
|
+
sanitized, _ = sanitize_diagnostic_text(stderr_detail)
|
|
108
|
+
return sanitized
|
|
97
109
|
|
|
98
110
|
|
|
99
111
|
def _build_clone_failure_log_summary(stderr_message: str, exit_code: int) -> str:
|
|
100
|
-
"""Return a
|
|
112
|
+
"""Return a one-line summary safe for error logs.
|
|
113
|
+
|
|
114
|
+
``stderr_message`` is already credential-stripped by
|
|
115
|
+
:func:`_build_clone_failure_message`; this re-runs the sanitizer defensively
|
|
116
|
+
in case a caller passes raw text.
|
|
117
|
+
"""
|
|
101
118
|
first_line = next(
|
|
102
119
|
(line.strip() for line in stderr_message.splitlines() if line.strip()), ""
|
|
103
120
|
)
|
|
104
121
|
if not first_line:
|
|
105
122
|
return f"git clone failed (exit {exit_code})"
|
|
106
|
-
|
|
123
|
+
sanitized, _ = sanitize_diagnostic_text(first_line)
|
|
124
|
+
return sanitized
|
|
107
125
|
|
|
108
126
|
|
|
109
127
|
async def clone_repo_with_progress(
|