datex-studio-cli 0.4.16__tar.gz → 0.4.18__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.
- datex_studio_cli-0.4.18/CONTEXT.md +13 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/PKG-INFO +2 -1
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/README.md +1 -0
- datex_studio_cli-0.4.18/otel/.env.example +14 -0
- datex_studio_cli-0.4.18/otel/Dockerfile +24 -0
- datex_studio_cli-0.4.18/otel/README.md +174 -0
- datex_studio_cli-0.4.18/otel/apply-local-cache.js +88 -0
- datex_studio_cli-0.4.18/otel/collector-config.yaml +145 -0
- datex_studio_cli-0.4.18/otel/docker-compose.yml +19 -0
- datex_studio_cli-0.4.18/otel/local-test-settings.json +26 -0
- datex_studio_cli-0.4.18/otel/remote-settings.json +36 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/pyproject.toml +1 -1
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/__init__.py +1 -1
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/auth.py +82 -19
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/branch.py +18 -3
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/cascade.py +9 -3
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/configuration.py +49 -16
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/datasource.py +47 -22
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/devops.py +227 -2
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/init.py +5 -1
- datex_studio_cli-0.4.18/src/dxs/commands/ng/api.py +334 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/ng/preview.py +259 -89
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/report/batch.py +1 -1
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/report/data.py +2 -2
- datex_studio_cli-0.4.18/src/dxs/commands/validation_report.py +122 -0
- datex_studio_cli-0.4.18/src/dxs/core/agent_browser.py +309 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/auth/datex_tenant.py +8 -10
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/auth/tenants.py +18 -1
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/datasource/flow_generator.py +116 -16
- datex_studio_cli-0.4.18/src/dxs/core/datasource/flow_shape.py +182 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/datasource/generator.py +13 -6
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/datasource/models.py +97 -5
- datex_studio_cli-0.4.18/src/dxs/core/datasource/validator.py +259 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/devops/client.py +41 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/devops/helpers.py +56 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/devops/models.py +130 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/ng/mocks.py +9 -0
- datex_studio_cli-0.4.18/src/dxs/ng/serve_lock.py +99 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/models.py +4 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/preview_arjs.py +10 -153
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/svg_renderer.py +6 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/paths.py +42 -0
- datex_studio_cli-0.4.16/src/dxs/commands/ng/api.py +0 -149
- datex_studio_cli-0.4.16/src/dxs/core/datasource/validator.py +0 -155
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/.gitignore +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/report-creator.skill +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/scripts/analyze_corpus.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/scripts/generate_rdlx_schema.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/__main__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/cli.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/api.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/config.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/crm.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/crm_delivery.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/crm_time.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/document.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/endpoint.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/env.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/explore.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/function.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/marketplace.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/ng/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/ng/_common.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/ng/create.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/ng/data.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/ng/pack.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/odata.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/organization.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/proxy.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/reference.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/release_notes.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/repo.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/report/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/report/add_cmds.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/report/api.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/report/create.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/report/dataset.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/report/edit.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/report/folder.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/report/preview.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/report/schema_cmds.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/reset.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/schema.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/servicepack.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/source.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/studio.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/telemetry.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/commands/user.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/context.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/api/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/api/app_config.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/api/client.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/api/endpoints.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/api/metadata_models.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/api/models.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/api/reference_graph.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/auth/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/auth/decorators.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/auth/msal_client.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/auth/token_cache.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/cache.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/datasource/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/datasource/flow_models.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/datasource/parsers.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/datasource/resolver.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/datasource/type_def_parser.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/designer/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/designer/flow_models.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/designer/models.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/designer/parsers.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/devops/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/dynamics/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/dynamics/client.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/dynamics/html.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/dynamics/metadata_cache.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/dynamics/resolvers.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/dynamics/time_entry.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/footprint/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/footprint/client.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/footprint/edmx_parser.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/footprint/metadata.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/function/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/function/generator.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/function/models.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/function/validator.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/graph.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/onboarding.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/output/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/output/csv_fmt.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/output/formatter.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/output/json_fmt.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/output/redaction.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/output/yaml_fmt.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/release_notes.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/responses.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/telemetry/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/telemetry/config.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/telemetry/identity.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/telemetry/recorder.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/telemetry/scrubber.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/core/telemetry/sender.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/models/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/ng/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/ng/folder.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/ng/manifest.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/ng/preview.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/ng/scaffold.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/py.typed +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/datasource_binding.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/engine.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/field_parser.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/folder.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/manifest.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/preview.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/schema.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/templates/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/templates/bill_of_lading.rdlx-json +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/templates/shipping_label.rdlx-json +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/validate_arjs.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/validator.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/report/wrapper.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/click_options.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/clipboard.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/config.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/errors.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/filtering.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/image.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/resolvers.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/responses.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/restricted.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/sorting.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/update_check.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/utils/versioning.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/__init__.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/app.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/design.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/proxy_app.py +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/404/index.html +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/404.html +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_buildManifest.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_ssgManifest.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/0b24cca5-c1e1c8810348f107.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/255-102f2e5b2e3dc2ef.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/538-84e2e111415f1fda.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/697-0e000ab410d9f470.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/871-1acacdb7b4022abf.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/8b8f67fc-6295949a4b5485a4.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/909-c88abba3cf0caec3.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/app/_not-found/page-ea1be7001c230704.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/app/design/capture/page-a5e129c2d223432c.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/app/design/page-21ba66a3dd3b03f0.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/app/layout-2cc6eac09e476c91.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/app/page-20c358395882cdac.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/framework-de98b93a850cfc71.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/main-0f18f91200dac003.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/main-app-b69998d8941231d8.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/chunks/webpack-2b297ada5306c17f.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/_next/static/css/360c657de5e04df8.css +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/design/capture/index.html +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/design/capture/index.txt +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/design/index.html +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/design/index.txt +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/index.html +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/index.txt +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/render-cli.js +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/report-preview.html +0 -0
- {datex_studio_cli-0.4.16 → datex_studio_cli-0.4.18}/src/dxs/web/static/report-validate.html +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Datex Studio CLI
|
|
2
|
+
|
|
3
|
+
The command-line interface (`dxs`) through which LLM agents and humans work with the Datex Studio low-code platform, plus the client side of its server counterpart, Quartermaster.
|
|
4
|
+
|
|
5
|
+
## Language
|
|
6
|
+
|
|
7
|
+
**Quartermaster**:
|
|
8
|
+
The Datex-owned service that is the dxs CLI's server-side counterpart — it issues supplies (skills, context) and keeps the records (telemetry, config index).
|
|
9
|
+
_Avoid_: companion service, dxs backend, dxs service
|
|
10
|
+
|
|
11
|
+
**Endpoint family**:
|
|
12
|
+
A cohesive group of Quartermaster endpoints serving one CLI capability (telemetry ingest, feedback, skills distribution, OData proxy, config indexing, context store). The unit in which Quartermaster is composed, enabled, and — when one earns it — deployed separately.
|
|
13
|
+
_Avoid_: module, microservice, subsystem
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: datex-studio-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.18
|
|
4
4
|
Summary: CLI for Datex Studio low-code platform, designed for LLM-based AI agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/datex/datex-studio-cli
|
|
6
6
|
Project-URL: Documentation, https://github.com/datex/datex-studio-cli
|
|
@@ -761,6 +761,7 @@ dxs api PUT /some/endpoint '{"data": "value"}'
|
|
|
761
761
|
### For Human Users
|
|
762
762
|
|
|
763
763
|
- **[Analysis Workflow](docs/analysis-workflow.md)** - Bulk analysis workflow documentation
|
|
764
|
+
- **[Claude Code Telemetry Collector](otel/README.md)** - Team-wide Claude Code observability: OTel collector → Datadog, rolled out from the claude.ai admin console
|
|
764
765
|
|
|
765
766
|
## Development
|
|
766
767
|
|
|
@@ -708,6 +708,7 @@ dxs api PUT /some/endpoint '{"data": "value"}'
|
|
|
708
708
|
### For Human Users
|
|
709
709
|
|
|
710
710
|
- **[Analysis Workflow](docs/analysis-workflow.md)** - Bulk analysis workflow documentation
|
|
711
|
+
- **[Claude Code Telemetry Collector](otel/README.md)** - Team-wide Claude Code observability: OTel collector → Datadog, rolled out from the claude.ai admin console
|
|
711
712
|
|
|
712
713
|
## Development
|
|
713
714
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Copy to .env and fill in. .env is gitignored -- never commit the real key.
|
|
2
|
+
#
|
|
3
|
+
# Datadog ingest API key (Organization Settings > API Keys).
|
|
4
|
+
DD_API_KEY=your-datadog-api-key-here
|
|
5
|
+
|
|
6
|
+
# Datadog site for your org. The Datex org is US1 - East, which is datadoghq.com.
|
|
7
|
+
# DatexCorp is US3 - West, which would be us3.datadoghq.com.
|
|
8
|
+
DD_SITE=datadoghq.com
|
|
9
|
+
|
|
10
|
+
# Bearer token Claude Code clients must present (their
|
|
11
|
+
# OTEL_EXPORTER_OTLP_HEADERS must carry the same value). The default below
|
|
12
|
+
# matches local-test-settings.json for local runs; for Azure generate a strong
|
|
13
|
+
# secret (e.g. `openssl rand -hex 32`) and store it as a container secret.
|
|
14
|
+
OTLP_BEARER_TOKEN=local-dev-token
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# OpenTelemetry Collector for Claude Code telemetry -> Datadog.
|
|
2
|
+
#
|
|
3
|
+
# The Datadog exporter lives only in the "contrib" distribution, not the core
|
|
4
|
+
# collector image.
|
|
5
|
+
#
|
|
6
|
+
# NOTE: pin this tag to a tested version (or a digest) before deploying to
|
|
7
|
+
# Azure. `latest` is fine for local verification, not for the team.
|
|
8
|
+
#
|
|
9
|
+
# Registry is fully qualified: podman does not default to Docker Hub for
|
|
10
|
+
# unqualified names, so a bare `otel/...` prompts or fails there.
|
|
11
|
+
FROM docker.io/otel/opentelemetry-collector-contrib:latest
|
|
12
|
+
|
|
13
|
+
# Copied to an explicit path so the config location never depends on the
|
|
14
|
+
# image's default, which differs between distributions. chown to the image's
|
|
15
|
+
# built-in non-root user so readability never depends on checkout file modes.
|
|
16
|
+
COPY --chown=10001:10001 collector-config.yaml /etc/otel/config.yaml
|
|
17
|
+
|
|
18
|
+
# 4318: OTLP http/protobuf (all Claude Code speaks).
|
|
19
|
+
# 13133: health_check extension, for orchestrator liveness/readiness probes.
|
|
20
|
+
EXPOSE 4318 13133
|
|
21
|
+
|
|
22
|
+
# DD_API_KEY, DD_SITE and OTLP_BEARER_TOKEN are supplied at runtime, never
|
|
23
|
+
# baked into the image.
|
|
24
|
+
CMD ["--config=/etc/otel/config.yaml"]
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Claude Code telemetry → Datadog
|
|
2
|
+
|
|
3
|
+
Centralised Claude Code observability for the team: an OpenTelemetry Collector holds the
|
|
4
|
+
Datadog credential, developer machines hold none, and configuration is pushed from the
|
|
5
|
+
claude.ai admin console rather than set up per-laptop.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Claude Code (each dev) --OTLP/http--> Collector (Azure) --datadog exporter--> Datadog
|
|
9
|
+
^ ^
|
|
10
|
+
| |
|
|
11
|
+
server-managed settings DD_API_KEY lives only here
|
|
12
|
+
(claude.ai admin console)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Files
|
|
16
|
+
|
|
17
|
+
| File | Purpose |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `Dockerfile` | Collector image (contrib distribution — the Datadog exporter is not in core) |
|
|
20
|
+
| `collector-config.yaml` | Receivers, processors, Datadog exporter for logs + traces + metrics |
|
|
21
|
+
| `docker-compose.yml` | Local verification harness |
|
|
22
|
+
| `.env.example` | Template for `DD_API_KEY` / `DD_SITE` / `OTLP_BEARER_TOKEN` — copy to `.env` (gitignored) |
|
|
23
|
+
| `remote-settings.json` | Payload for the claude.ai admin console |
|
|
24
|
+
| `local-test-settings.json` | Same env block pointed at `localhost` for local testing |
|
|
25
|
+
| `apply-local-cache.js` | Loads `local-test-settings.json` into Claude Code's settings cache for a local test run |
|
|
26
|
+
|
|
27
|
+
## Verify locally
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
cd otel
|
|
31
|
+
cp .env.example .env # fill in DD_API_KEY
|
|
32
|
+
docker compose up --build
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then load the settings and restart:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
node apply-local-cache.js # writes ~/.claude/remote-settings.json, checks the collector
|
|
39
|
+
# --revert restores it to {}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Restart Claude Code immediately afterwards.** Two reasons: OpenTelemetry configuration is
|
|
43
|
+
only read at startup, and Claude Code refetches server-managed settings in the background —
|
|
44
|
+
when the org has no server-side config that refetch overwrites the cache with `{}`. A
|
|
45
|
+
hand-placed payload therefore survives exactly one session and must be re-applied before
|
|
46
|
+
each restart. Approve the `OTEL_EXPORTER_OTLP_ENDPOINT` dialog on startup or Claude Code
|
|
47
|
+
exits.
|
|
48
|
+
|
|
49
|
+
This one-session-per-edit friction disappears once the payload is delivered from the admin
|
|
50
|
+
console for real.
|
|
51
|
+
|
|
52
|
+
Confirm in Datadog under **Logs** (not APM) with `service:claude-code`. Collector-side, the
|
|
53
|
+
`debug` exporter prints per-batch item **counts** to container stdout — enough to confirm
|
|
54
|
+
data is flowing. It deliberately does not print payloads; see the verbosity note in
|
|
55
|
+
`collector-config.yaml` before raising it:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
docker compose logs -f otel-collector
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Hand off to the team
|
|
62
|
+
|
|
63
|
+
1. **Decide the data-governance posture first** (see the Data governance section below):
|
|
64
|
+
keep or drop `OTEL_LOG_TOOL_CONTENT` in `remote-settings.json` *before* anything ships.
|
|
65
|
+
Raw API bodies (`OTEL_LOG_RAW_API_BODIES`) are already off; prompts, responses and tool
|
|
66
|
+
content still land in Datadog.
|
|
67
|
+
2. Pin the base image in `Dockerfile` to a tested tag or digest — it currently says
|
|
68
|
+
`latest`. Then build and push to ACR, deploy as an Azure Container App or App Service.
|
|
69
|
+
Give the container an explicit memory limit and set `GOMEMLIMIT` to ~80% of it — the
|
|
70
|
+
`memory_limiter` processor depends on a real limit (`docker-compose.yml` shows the
|
|
71
|
+
pattern). Point liveness/readiness probes at the `health_check` extension on port 13133.
|
|
72
|
+
3. Terminate TLS in front of it. **OTLP payloads carry full prompts, file contents and
|
|
73
|
+
responses** — this must not travel over plain HTTP. The endpoint pushed to clients is
|
|
74
|
+
the TLS front-end URL (Azure Container Apps ingress listens on 443), not `:4318` —
|
|
75
|
+
ingress targets the collector's internal 4318.
|
|
76
|
+
4. Set `DD_API_KEY`, `DD_SITE` and `OTLP_BEARER_TOKEN` as container secrets, not image
|
|
77
|
+
layers. Generate the token with e.g. `openssl rand -hex 32`.
|
|
78
|
+
5. Restrict ingress to corporate network / VPN. The bearer token the receiver now requires
|
|
79
|
+
is defense in depth on top of that — a shared, developer-visible secret, not a
|
|
80
|
+
substitute for network restriction.
|
|
81
|
+
6. An Owner pastes `remote-settings.json` into
|
|
82
|
+
**claude.ai → Admin Settings → Claude Code → Managed settings**, replacing
|
|
83
|
+
`<COLLECTOR-PUBLIC-URL>` and `<OTLP-BEARER-TOKEN>`.
|
|
84
|
+
|
|
85
|
+
Each developer sees a **one-time approval dialog** for the endpoint variable. If they
|
|
86
|
+
decline, Claude Code exits. This is unavoidable — Claude Code always prompts for a
|
|
87
|
+
non-empty `OTEL_EXPORTER_OTLP_ENDPOINT`.
|
|
88
|
+
|
|
89
|
+
## Where prompts and responses land
|
|
90
|
+
|
|
91
|
+
Verified against Claude Code 2.1.220 through this collector. **Both prompts and response
|
|
92
|
+
text are captured.**
|
|
93
|
+
|
|
94
|
+
| Content | Attribute | Event |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| Prompt text | `prompt_text` | `user_prompt` |
|
|
97
|
+
| Prompt uuid | `prompt.id` | all events |
|
|
98
|
+
| Assistant response text | `response`, `response_length` | `assistant_response` |
|
|
99
|
+
| Tool calls / inputs | `tool_input`, `tool_parameters` | `tool_result` |
|
|
100
|
+
| Cost / tokens | `cost_usd`, `input_tokens`, `output_tokens` | `api_request` |
|
|
101
|
+
| Thinking | `content[].thinking` | always `<REDACTED>` |
|
|
102
|
+
|
|
103
|
+
### Why the collector is required, not just convenient
|
|
104
|
+
|
|
105
|
+
Sending straight to Datadog's OTLP intake (`https://otlp.datadoghq.com`) loses data. Two
|
|
106
|
+
separate defects, both fixed here:
|
|
107
|
+
|
|
108
|
+
**1. Prompt text destroyed by an attribute collision.** Claude Code emits `prompt` (the
|
|
109
|
+
text) *and* `prompt.id` (a uuid). Datadog flattens dotted keys into nested objects, so
|
|
110
|
+
`prompt.id` becomes an object at `prompt` and the sibling string is silently discarded —
|
|
111
|
+
`prompt_length` still reports the real size, which is how we caught it. The `transform`
|
|
112
|
+
processor in `collector-config.yaml` copies the string to `prompt_text` and drops the
|
|
113
|
+
colliding key.
|
|
114
|
+
|
|
115
|
+
Slash-command prompts were never affected, because command dispatches carry no `prompt.id`
|
|
116
|
+
and therefore no collision. That asymmetry — "prompt text only shows up for slash
|
|
117
|
+
commands" — is the signature of this bug.
|
|
118
|
+
|
|
119
|
+
**2. `assistant_response` events dropped in transit.** With the direct endpoint these never
|
|
120
|
+
arrived, across hours of querying. Through the collector they arrive with a populated
|
|
121
|
+
`response` attribute. Same flags, same client version — the only change was the endpoint,
|
|
122
|
+
so Datadog's OTLP intake appears to drop them. The collector's `datadog` exporter forwards
|
|
123
|
+
them intact.
|
|
124
|
+
|
|
125
|
+
### Query gotchas
|
|
126
|
+
|
|
127
|
+
- **`prompt_length` without `prompt_text`** means the transform isn't running — that is the
|
|
128
|
+
collision, not redaction.
|
|
129
|
+
- **Search by attribute, not free text.** `@marker:VALUE` works; a bare string only matches
|
|
130
|
+
the log message body, which is usually just the event name.
|
|
131
|
+
- **No `api_request_body` / `api_response_body` events** — `OTEL_LOG_RAW_API_BODIES` is
|
|
132
|
+
deliberately off: request bodies repeat the whole conversation on every API call (pure
|
|
133
|
+
ingest cost, plus one more copy of any secret in context), and `user_prompt` +
|
|
134
|
+
`assistant_response` + the tool events already reconstruct the transcript. If raw bodies
|
|
135
|
+
are ever needed, re-enable the flag in `remote-settings.json` and note request bodies
|
|
136
|
+
arrive truncated at the 60 KB `CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTH` limit (invalid
|
|
137
|
+
JSON — filter on `body_truncated`).
|
|
138
|
+
|
|
139
|
+
A `Stop` hook reading `last_assistant_message` is **not needed**. That was a workaround
|
|
140
|
+
built before `assistant_response` was confirmed working through the collector. An
|
|
141
|
+
implementation is kept at `~/.claude/hooks/dd-transcript.js` (Delian's machine,
|
|
142
|
+
unregistered) but is not part of this setup.
|
|
143
|
+
|
|
144
|
+
## Enforcement reality check
|
|
145
|
+
|
|
146
|
+
Server-managed settings are, per Anthropic's docs, "a client-side control, not a security
|
|
147
|
+
boundary. On unmanaged devices, a user doesn't need admin or sudo access to bypass them."
|
|
148
|
+
|
|
149
|
+
| Bypass | Effect |
|
|
150
|
+
|---|---|
|
|
151
|
+
| Delete `~/.claude/remote-settings.json` | Brief unenforced window at next start |
|
|
152
|
+
| Run an older Claude Code | Never fetches managed settings |
|
|
153
|
+
| Set `ANTHROPIC_BASE_URL` or `CLAUDE_CODE_USE_BEDROCK` | Settings fetch skipped entirely |
|
|
154
|
+
|
|
155
|
+
`forceRemoteSettingsRefresh` and `requiredMinimumVersion` (both already in
|
|
156
|
+
`remote-settings.json`) close the easy gaps. For genuine enforcement, deploy
|
|
157
|
+
endpoint-managed settings by MDM to `C:\Program Files\ClaudeCode\managed-settings.json`
|
|
158
|
+
— note that `C:\ProgramData\ClaudeCode\` stopped being read in v2.1.75.
|
|
159
|
+
|
|
160
|
+
**Server-managed and endpoint-managed settings do not merge.** If server-managed delivers
|
|
161
|
+
any keys at all, the MDM file is ignored completely. Pick one as the source of truth.
|
|
162
|
+
|
|
163
|
+
## Data governance
|
|
164
|
+
|
|
165
|
+
With these flags on, Datadog receives prompts, responses, and tool inputs and outputs —
|
|
166
|
+
including the contents of any file Claude Code reads. In `datex-application-api` that means
|
|
167
|
+
connection strings and `Azure__ClientSecret` can land in Datadog logs. Every event also
|
|
168
|
+
carries the developer's email and account UUIDs. (Raw API bodies —
|
|
169
|
+
`OTEL_LOG_RAW_API_BODIES` — are deliberately off: they would re-send the whole conversation
|
|
170
|
+
on every API call for no analytical gain.)
|
|
171
|
+
|
|
172
|
+
Decide deliberately whether that is acceptable before rolling out, and consider dropping
|
|
173
|
+
`OTEL_LOG_TOOL_CONTENT` if it is not. Datadog's Sensitive Data Scanner can redact patterns
|
|
174
|
+
at ingest as a second layer.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Local testing helper.
|
|
4
|
+
*
|
|
5
|
+
* Copies local-test-settings.json into Claude Code's server-managed settings
|
|
6
|
+
* cache (~/.claude/remote-settings.json), simulating a policy pushed from the
|
|
7
|
+
* admin console.
|
|
8
|
+
*
|
|
9
|
+
* Why this is needed: Claude Code applies the cache at startup, then refetches
|
|
10
|
+
* from Anthropic in the background. When the org has no server-managed config,
|
|
11
|
+
* that refetch overwrites the cache with {} -- so a hand-placed payload is good
|
|
12
|
+
* for exactly one session and must be re-applied before each restart.
|
|
13
|
+
*
|
|
14
|
+
* Usage: node apply-local-cache.js [--revert]
|
|
15
|
+
*
|
|
16
|
+
* Restart Claude Code IMMEDIATELY after running this. OpenTelemetry settings
|
|
17
|
+
* are only read at startup, and the background fetch will blank the file.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const http = require('http');
|
|
23
|
+
|
|
24
|
+
const HOME = process.env.USERPROFILE || process.env.HOME;
|
|
25
|
+
const CACHE = path.join(HOME, '.claude', 'remote-settings.json');
|
|
26
|
+
const SOURCE = path.join(__dirname, 'local-test-settings.json');
|
|
27
|
+
|
|
28
|
+
if (process.argv.includes('--revert')) {
|
|
29
|
+
fs.writeFileSync(CACHE, '{}\n');
|
|
30
|
+
console.log(`reverted ${CACHE} to {}`);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const source = JSON.parse(fs.readFileSync(SOURCE, 'utf8'));
|
|
35
|
+
|
|
36
|
+
// Drop the _comment-style documentation keys; they would be stripped by
|
|
37
|
+
// Claude Code's schema validation anyway.
|
|
38
|
+
const payload = Object.fromEntries(Object.entries(source).filter(([k]) => !k.startsWith('_')));
|
|
39
|
+
|
|
40
|
+
if (!payload.env || !Object.keys(payload.env).length) {
|
|
41
|
+
console.error(`no env block found in ${SOURCE}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fs.writeFileSync(CACHE, JSON.stringify(payload, null, 2) + '\n');
|
|
46
|
+
console.log(`wrote ${Object.keys(payload.env).length} env vars to ${CACHE}`);
|
|
47
|
+
|
|
48
|
+
const endpoint = payload.env.OTEL_EXPORTER_OTLP_ENDPOINT || '';
|
|
49
|
+
console.log(`endpoint: ${endpoint}`);
|
|
50
|
+
|
|
51
|
+
// Check the collector is actually listening -- restarting into a dead endpoint
|
|
52
|
+
// wastes a whole session, since the payload only survives one startup.
|
|
53
|
+
const m = endpoint.match(/^http:\/\/([^:/]+):(\d+)/);
|
|
54
|
+
if (!m) {
|
|
55
|
+
console.log('\nendpoint is not local http; skipping reachability check');
|
|
56
|
+
return finish();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Send the same auth header the client will use, so the probe exercises the
|
|
60
|
+
// collector's bearertokenauth too. 401 = token mismatch, not unreachable.
|
|
61
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
62
|
+
for (const pair of (payload.env.OTEL_EXPORTER_OTLP_HEADERS || '').split(',')) {
|
|
63
|
+
const i = pair.indexOf('=');
|
|
64
|
+
if (i > 0) headers[pair.slice(0, i).trim()] = pair.slice(i + 1).trim();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const req = http.request(
|
|
68
|
+
{ method: 'POST', hostname: m[1], port: Number(m[2]), path: '/v1/logs', headers, timeout: 4000 },
|
|
69
|
+
(res) => {
|
|
70
|
+
res.resume();
|
|
71
|
+
console.log(res.statusCode === 401
|
|
72
|
+
? 'collector: reachable but rejected the bearer token (HTTP 401) -- OTEL_EXPORTER_OTLP_HEADERS must match the collector OTLP_BEARER_TOKEN'
|
|
73
|
+
: `collector: reachable (HTTP ${res.statusCode})`);
|
|
74
|
+
finish();
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
req.on('error', (e) => {
|
|
78
|
+
console.log(`collector: NOT reachable (${e.code})`);
|
|
79
|
+
console.log(' start it first: podman start datex-otel-collector');
|
|
80
|
+
finish();
|
|
81
|
+
});
|
|
82
|
+
req.on('timeout', () => { req.destroy(); console.log('collector: timeout'); finish(); });
|
|
83
|
+
req.end('{}');
|
|
84
|
+
|
|
85
|
+
function finish() {
|
|
86
|
+
console.log('\nRestart Claude Code now -- the background fetch will blank this file.');
|
|
87
|
+
console.log('Approve the OTEL_EXPORTER_OTLP_ENDPOINT dialog on startup, or Claude Code exits.');
|
|
88
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# OpenTelemetry Collector configuration for Claude Code telemetry -> Datadog.
|
|
2
|
+
#
|
|
3
|
+
# Claude Code clients send OTLP over http/protobuf to this collector; the
|
|
4
|
+
# collector holds the only copy of the Datadog API key. Developer machines
|
|
5
|
+
# never see a credential.
|
|
6
|
+
|
|
7
|
+
extensions:
|
|
8
|
+
# Liveness/readiness endpoint for orchestrator probes (Azure Container Apps)
|
|
9
|
+
# and local checks: GET http://localhost:13133/health
|
|
10
|
+
health_check:
|
|
11
|
+
endpoint: 0.0.0.0:13133
|
|
12
|
+
|
|
13
|
+
# Clients must present this token (pushed to them as OTEL_EXPORTER_OTLP_HEADERS
|
|
14
|
+
# in the same settings payload). Defense in depth on top of network/ingress
|
|
15
|
+
# restriction -- the receiver is the sole write path into the Datadog org.
|
|
16
|
+
bearertokenauth:
|
|
17
|
+
scheme: Bearer
|
|
18
|
+
token: ${env:OTLP_BEARER_TOKEN}
|
|
19
|
+
|
|
20
|
+
receivers:
|
|
21
|
+
# http/protobuf only -- it is all Claude Code speaks. No gRPC listener:
|
|
22
|
+
# an unused port on this receiver is pure attack surface.
|
|
23
|
+
otlp:
|
|
24
|
+
protocols:
|
|
25
|
+
http:
|
|
26
|
+
endpoint: 0.0.0.0:4318
|
|
27
|
+
auth:
|
|
28
|
+
authenticator: bearertokenauth
|
|
29
|
+
|
|
30
|
+
processors:
|
|
31
|
+
# First in every pipeline: refuse new data instead of dying when memory runs
|
|
32
|
+
# hot. Percentages are relative to the container memory limit -- always run
|
|
33
|
+
# this image with an explicit limit and GOMEMLIMIT at ~80% of it
|
|
34
|
+
# (docker-compose.yml shows the pattern for local).
|
|
35
|
+
memory_limiter:
|
|
36
|
+
check_interval: 1s
|
|
37
|
+
limit_percentage: 80
|
|
38
|
+
spike_limit_percentage: 25
|
|
39
|
+
|
|
40
|
+
# Claude Code sends no hostname, which makes Datadog tag every span
|
|
41
|
+
# issue_type:empty_hostname. Detecting it here fixes grouping.
|
|
42
|
+
#
|
|
43
|
+
# CAVEAT: this reports the COLLECTOR's hostname, not the developer's. With a
|
|
44
|
+
# single shared collector every developer's telemetry carries the same
|
|
45
|
+
# host.name. Group per developer by user.email or session.id instead. Drop
|
|
46
|
+
# this processor if host-level grouping matters more than filling the field.
|
|
47
|
+
resource_detection:
|
|
48
|
+
detectors: [env, system]
|
|
49
|
+
system:
|
|
50
|
+
hostname_sources: [os]
|
|
51
|
+
|
|
52
|
+
# Claude Code sends BOTH `prompt` (the prompt text) and `prompt.id` (a uuid).
|
|
53
|
+
# Datadog flattens dotted keys into nested objects, so `prompt.id` becomes an
|
|
54
|
+
# object at `prompt` and silently discards the sibling string -- the prompt
|
|
55
|
+
# text vanishes while `prompt_length` still reports its real size.
|
|
56
|
+
#
|
|
57
|
+
# Renaming the string before export sidesteps the collision. Slash-command
|
|
58
|
+
# prompts were unaffected because command dispatches carry no `prompt.id`,
|
|
59
|
+
# which is why prompt text only ever showed up for them.
|
|
60
|
+
transform:
|
|
61
|
+
error_mode: ignore
|
|
62
|
+
log_statements:
|
|
63
|
+
- context: log
|
|
64
|
+
statements:
|
|
65
|
+
- set(attributes["prompt_text"], attributes["prompt"]) where IsString(attributes["prompt"])
|
|
66
|
+
- delete_key(attributes, "prompt") where attributes["prompt_text"] != nil
|
|
67
|
+
# The traces beta puts prompt/tool content on spans and span events
|
|
68
|
+
# (OTEL_LOG_TOOL_CONTENT). If those carry the same prompt + prompt.id pair,
|
|
69
|
+
# the identical flattening collision recurs in Datadog APM. Precautionary
|
|
70
|
+
# mirror -- verify against a live trace; drop if spans never carry `prompt`.
|
|
71
|
+
trace_statements:
|
|
72
|
+
- context: span
|
|
73
|
+
statements:
|
|
74
|
+
- set(attributes["prompt_text"], attributes["prompt"]) where IsString(attributes["prompt"])
|
|
75
|
+
- delete_key(attributes, "prompt") where attributes["prompt_text"] != nil
|
|
76
|
+
- context: spanevent
|
|
77
|
+
statements:
|
|
78
|
+
- set(attributes["prompt_text"], attributes["prompt"]) where IsString(attributes["prompt"])
|
|
79
|
+
- delete_key(attributes, "prompt") where attributes["prompt_text"] != nil
|
|
80
|
+
|
|
81
|
+
# Sized to Datadog's documented example for this exporter. Batch limits count
|
|
82
|
+
# ITEMS, not bytes -- content-bearing events run up to 60 KB each, and
|
|
83
|
+
# oversized batches can exceed Datadog's ~5 MB uncompressed logs intake cap.
|
|
84
|
+
batch:
|
|
85
|
+
send_batch_size: 10
|
|
86
|
+
send_batch_max_size: 100
|
|
87
|
+
timeout: 10s
|
|
88
|
+
|
|
89
|
+
exporters:
|
|
90
|
+
datadog:
|
|
91
|
+
api:
|
|
92
|
+
key: ${env:DD_API_KEY}
|
|
93
|
+
site: ${env:DD_SITE}
|
|
94
|
+
# In-memory queue only -- diskless by choice. Failure behavior:
|
|
95
|
+
# - retries never give up on elapsed time (max_elapsed_time: 0), so a
|
|
96
|
+
# Datadog outage of any length is survived as long as the queue holds
|
|
97
|
+
# (permanent 4xx errors are still dropped, not retried -- a bad API key
|
|
98
|
+
# can't jam the queue)
|
|
99
|
+
# - the queue absorbs outages/bursts; memory_limiter stops intake before
|
|
100
|
+
# queue growth can OOM the container
|
|
101
|
+
# - when the queue IS full, new data drops and
|
|
102
|
+
# otelcol_exporter_enqueue_failed_* counts it -- alert on that metric
|
|
103
|
+
# - a container restart loses whatever is queued; accepted cost of
|
|
104
|
+
# staying diskless
|
|
105
|
+
retry_on_failure:
|
|
106
|
+
enabled: true
|
|
107
|
+
initial_interval: 5s
|
|
108
|
+
max_interval: 60s
|
|
109
|
+
max_elapsed_time: 0
|
|
110
|
+
sending_queue:
|
|
111
|
+
enabled: true
|
|
112
|
+
num_consumers: 10
|
|
113
|
+
queue_size: 5000
|
|
114
|
+
|
|
115
|
+
# Wired into every pipeline so container logs confirm data is arriving.
|
|
116
|
+
#
|
|
117
|
+
# verbosity: basic logs per-batch item COUNTS only. Do not raise this to
|
|
118
|
+
# detailed outside short local debugging -- detailed writes full payloads to
|
|
119
|
+
# container stdout, and those payloads contain prompts, responses and the
|
|
120
|
+
# contents of any file Claude Code read.
|
|
121
|
+
debug:
|
|
122
|
+
verbosity: basic
|
|
123
|
+
|
|
124
|
+
service:
|
|
125
|
+
extensions: [health_check, bearertokenauth]
|
|
126
|
+
|
|
127
|
+
pipelines:
|
|
128
|
+
logs:
|
|
129
|
+
receivers: [otlp]
|
|
130
|
+
processors: [memory_limiter, resource_detection, transform, batch]
|
|
131
|
+
exporters: [datadog, debug]
|
|
132
|
+
|
|
133
|
+
traces:
|
|
134
|
+
receivers: [otlp]
|
|
135
|
+
processors: [memory_limiter, resource_detection, transform, batch]
|
|
136
|
+
exporters: [datadog, debug]
|
|
137
|
+
|
|
138
|
+
metrics:
|
|
139
|
+
receivers: [otlp]
|
|
140
|
+
processors: [memory_limiter, resource_detection, batch]
|
|
141
|
+
exporters: [datadog, debug]
|
|
142
|
+
|
|
143
|
+
telemetry:
|
|
144
|
+
logs:
|
|
145
|
+
level: info
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Local verification harness. Azure deployment uses the image directly.
|
|
2
|
+
services:
|
|
3
|
+
otel-collector:
|
|
4
|
+
build: .
|
|
5
|
+
image: datex-claude-otel-collector:local
|
|
6
|
+
container_name: datex-otel-collector
|
|
7
|
+
env_file:
|
|
8
|
+
- .env
|
|
9
|
+
environment:
|
|
10
|
+
# ~80% of mem_limit, so Go's GC cooperates with the memory_limiter
|
|
11
|
+
# processor instead of the kernel OOM-killing the container.
|
|
12
|
+
GOMEMLIMIT: 400MiB
|
|
13
|
+
ports:
|
|
14
|
+
# Loopback only: the receiver is a write path into the org's Datadog
|
|
15
|
+
# account -- don't expose it to every peer on the office/VPN network.
|
|
16
|
+
- "127.0.0.1:4318:4318"
|
|
17
|
+
- "127.0.0.1:13133:13133"
|
|
18
|
+
mem_limit: 512m
|
|
19
|
+
restart: unless-stopped
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Local verification only. Merge this env block into your own ~/.claude/settings.json while testing the collector on localhost, then remove it once server-managed settings are live. Same keys as remote-settings.json except OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS (local-dev token, matching .env.example) and deployment.environment. Again no Datadog key -- the collector holds it.",
|
|
3
|
+
|
|
4
|
+
"env": {
|
|
5
|
+
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
|
|
6
|
+
"CLAUDE_CODE_ENHANCED_TELEMETRY_BETA": "1",
|
|
7
|
+
|
|
8
|
+
"OTEL_LOGS_EXPORTER": "otlp",
|
|
9
|
+
"OTEL_TRACES_EXPORTER": "otlp",
|
|
10
|
+
"OTEL_METRICS_EXPORTER": "otlp",
|
|
11
|
+
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
|
|
12
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4318",
|
|
13
|
+
"OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer local-dev-token",
|
|
14
|
+
|
|
15
|
+
"OTEL_METRIC_EXPORT_INTERVAL": "60000",
|
|
16
|
+
"OTEL_METRICS_INCLUDE_SESSION_ID": "true",
|
|
17
|
+
|
|
18
|
+
"OTEL_SERVICE_NAME": "claude-code",
|
|
19
|
+
"OTEL_RESOURCE_ATTRIBUTES": "deployment.environment=local,service.version=1.0",
|
|
20
|
+
|
|
21
|
+
"OTEL_LOG_USER_PROMPTS": "1",
|
|
22
|
+
"OTEL_LOG_ASSISTANT_RESPONSES": "1",
|
|
23
|
+
"OTEL_LOG_TOOL_DETAILS": "1",
|
|
24
|
+
"OTEL_LOG_TOOL_CONTENT": "1"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Paste the contents of this file (minus the _comment keys) into claude.ai > Admin Settings > Claude Code > Managed settings. Requires Owner or Primary Owner role and a Claude for Teams/Enterprise plan. The only secret here is the shared collector bearer token; the Datadog API key lives only on the collector.",
|
|
3
|
+
|
|
4
|
+
"_endpoint_note": "Replace <COLLECTOR-PUBLIC-URL> with the collector's public TLS URL (e.g. https://otel.example.com). That is the TLS front end -- Azure Container Apps ingress listens on 443 -- NOT the collector's internal port 4318, which ingress targets. Must be HTTPS in production: OTLP payloads carry full prompts and responses. Claude Code always shows each developer a one-time approval dialog for a non-empty OTEL_EXPORTER_OTLP_ENDPOINT; if they decline, Claude Code exits.",
|
|
5
|
+
|
|
6
|
+
"_auth_note": "Replace <OTLP-BEARER-TOKEN> with the token configured on the collector (its OTLP_BEARER_TOKEN secret). It is a shared, developer-visible secret: defense in depth on top of ingress restriction, not a substitute for it.",
|
|
7
|
+
|
|
8
|
+
"env": {
|
|
9
|
+
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
|
|
10
|
+
"CLAUDE_CODE_ENHANCED_TELEMETRY_BETA": "1",
|
|
11
|
+
|
|
12
|
+
"OTEL_LOGS_EXPORTER": "otlp",
|
|
13
|
+
"OTEL_TRACES_EXPORTER": "otlp",
|
|
14
|
+
"OTEL_METRICS_EXPORTER": "otlp",
|
|
15
|
+
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
|
|
16
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT": "https://<COLLECTOR-PUBLIC-URL>",
|
|
17
|
+
"OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer <OTLP-BEARER-TOKEN>",
|
|
18
|
+
|
|
19
|
+
"OTEL_METRIC_EXPORT_INTERVAL": "60000",
|
|
20
|
+
"OTEL_METRICS_INCLUDE_SESSION_ID": "true",
|
|
21
|
+
|
|
22
|
+
"OTEL_SERVICE_NAME": "claude-code",
|
|
23
|
+
"OTEL_RESOURCE_ATTRIBUTES": "deployment.environment=dev,service.version=1.0",
|
|
24
|
+
|
|
25
|
+
"OTEL_LOG_USER_PROMPTS": "1",
|
|
26
|
+
"OTEL_LOG_ASSISTANT_RESPONSES": "1",
|
|
27
|
+
"OTEL_LOG_TOOL_DETAILS": "1",
|
|
28
|
+
"OTEL_LOG_TOOL_CONTENT": "1"
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
"_metrics_note": "Metrics use Claude Code's default delta temporality (what Datadog expects) and the default-off app.version attribute. session.id is kept on metrics for session-level drill-down -- an accepted Datadog custom-metric cardinality cost, since every session mints new timeseries; set it to false if the custom-metrics bill grows. Per-developer analysis uses user.email either way.",
|
|
32
|
+
|
|
33
|
+
"_enforcement_note": "forceRemoteSettingsRefresh makes the CLI exit rather than run unenforced when the settings fetch fails. requiredMinimumVersion stops developers dropping to a build that ignores server-managed settings (NOTE: this key is not in Anthropic's public docs -- confirm it is honored, or it may be silently stripped). Neither makes this a security boundary -- see README.",
|
|
34
|
+
"forceRemoteSettingsRefresh": true,
|
|
35
|
+
"requiredMinimumVersion": "2.1.220"
|
|
36
|
+
}
|