tripl 0.2.2__tar.gz → 0.3.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.
- {tripl-0.2.2 → tripl-0.3.0}/PKG-INFO +8 -8
- {tripl-0.2.2 → tripl-0.3.0}/README.md +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/pyproject.toml +12 -12
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/__init__.py +2 -2
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/__init__.py +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/auth.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/branches.py +2 -2
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/event_types.py +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/events.py +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/scans.py +2 -2
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/search.py +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/render.py +2 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/cli.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/client.py +2 -2
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/__init__.py +4 -4
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/_plan.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/_resolve.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/_write.py +2 -2
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/doctor.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/drifts.py +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/events.py +2 -3
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/install.py +48 -8
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/plan.py +2 -3
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/scans.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/upgrade.py +9 -10
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/watch.py +1 -2
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/config.py +2 -2
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/diagnostics/__init__.py +2 -2
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/diagnostics/checks.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/diagnostics/collect.py +5 -5
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/diagnostics/endpoints.py +6 -6
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/diagnostics/scan_checks.py +5 -6
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/errors.py +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/install/__init__.py +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/install/docker.py +2 -2
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/install/files/compose.yaml +44 -17
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/install/files/rabbitmq.conf +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/install/files.py +23 -5
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/install/health.py +2 -2
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/install/plan.py +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/install/render.py +7 -7
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/install/secrets.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/install/shell.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/model.py +9 -9
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/render.py +4 -4
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/report.py +7 -7
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/runner.py +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/watch/__init__.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/watch/collect.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/watch/diff.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/watch/model.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/tests/conftest.py +2 -3
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_api_requests.py +4 -4
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_checks.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_cli.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_client.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_contract.py +30 -32
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_docker_detect.py +1 -2
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_doctor.py +2 -2
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_drifts_cmd.py +2 -2
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_events_cmd.py +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_install_cmd.py +58 -9
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_install_files.py +24 -8
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_output_samples.py +4 -4
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_plan_cmd.py +4 -4
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_runner.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_scans_cmd.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_upgrade_cmd.py +8 -9
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_watch.py +3 -3
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_watch_diff.py +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/uv.lock +1 -1
- {tripl-0.2.2 → tripl-0.3.0}/.gitignore +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/LICENSE +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/__main__.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/chart_annotations.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/data_sources.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/docs.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/endpoints.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/monitoring.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/plan_export.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/plan_validation.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/projects.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/request.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/api/variables.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/__init__.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/calls.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/config.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/lexer.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/model.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/payloads.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/presets.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/scan.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/symbols.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/validate.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/values.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/check/yamlish.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/__init__.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/calls.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/context.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/context_named.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/context_plan.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/files.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/generate.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/languages.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/model.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/naming.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/template.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/named.kotlin.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/named.swift.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/named.ts.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/screen_view.kotlin.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/screen_view.swift.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/screen_view.ts.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/self_describing.kotlin.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/self_describing.swift.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/self_describing.ts.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/structured.kotlin.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/structured.swift.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/structured.ts.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/transport.kotlin.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/transport.swift.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/templates/transport.ts.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/codegen/value_types.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/_docs_files.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/annotate.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/check.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/codegen.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/docs.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/export.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/status.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/commands/whoami.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/py.typed +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/watch/loop.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/src/tripl_cli/watch/render.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/__init__.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/check.yml +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/check.yml +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/kotlin/AcmeAction.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/kotlin/AcmeAppConstants.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/kotlin/AcmeCategory.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/kotlin/AcmeLabel.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/kotlin/AcmeProperty.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/kotlin/AcmeScreens.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/kotlin/AcmeStructured.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/kotlin/TriplTransport.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/swift/AcmeAppConstants.swift +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/swift/AcmePlanned.swift +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/swift/AcmeScreens.swift +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/swift/AcmeStructured.swift +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/swift/TriplTransport.swift +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/ts/acmeAction.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/ts/acmeAppConstants.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/ts/acmeCategory.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/ts/acmeLabel.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/ts/acmeProperty.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/ts/acmeScreens.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/ts/acmeStructured.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/golden/ts/triplTransport.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/model.json +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/holder.kotlin.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/holder.ts.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/named.kotlin.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/named.swift.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/named.ts.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/planned.swift.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/screen.kotlin.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/screen.swift.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/screen.ts.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/structured.kotlin.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/structured.swift.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/custom/templates/structured.ts.mustache +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/kotlin/AppEvents.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/kotlin/LegacyTracking.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/kotlin/ScreenTracking.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/kotlin/SdTracking.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/kotlin/TriplTransport.kt +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/swift/AppEvents.swift +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/swift/LegacyTracking.swift +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/swift/ScreenTracking.swift +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/swift/SdTracking.swift +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/swift/TriplTransport.swift +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/ts/appEvents.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/ts/legacyTracking.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/ts/screenTracking.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/ts/sdTracking.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/golden/ts/triplTransport.ts +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/codegen/model.json +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_annotate_cmd.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_check_cmd.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_check_scan.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_codegen_cmd.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_codegen_custom.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_codegen_generate.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_codegen_naming.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_codegen_template.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_config.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_docs_cmd.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_status.py +0 -0
- {tripl-0.2.2 → tripl-0.3.0}/tests/test_whoami_cmd.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tripl
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Operator CLI for a tripl tracking-plan instance: diagnostics, health and live monitoring
|
|
5
|
-
Project-URL: Homepage, https://
|
|
6
|
-
Project-URL: Documentation, https://
|
|
7
|
-
Project-URL: Repository, https://github.com/
|
|
8
|
-
Project-URL: Issues, https://github.com/
|
|
5
|
+
Project-URL: Homepage, https://docs.tripl.io/
|
|
6
|
+
Project-URL: Documentation, https://docs.tripl.io/run/cli
|
|
7
|
+
Project-URL: Repository, https://github.com/tripl-io/tripl
|
|
8
|
+
Project-URL: Issues, https://github.com/tripl-io/tripl/issues
|
|
9
9
|
Author: Vladislav Denisov
|
|
10
10
|
License-Expression: Apache-2.0
|
|
11
11
|
License-File: LICENSE
|
|
@@ -38,7 +38,7 @@ import package is **`tripl_cli`**. The last one is deliberate: the service's own
|
|
|
38
38
|
source package is `backend/src/tripl/`, so a distribution that installed an
|
|
39
39
|
importable `tripl` would shadow it in any environment holding both — a
|
|
40
40
|
contributor's backend venv, for one. The service is packaged as the separate
|
|
41
|
-
`tripl-server` distribution
|
|
41
|
+
`tripl-server` distribution, so `tripl` names this CLI alone.
|
|
42
42
|
|
|
43
43
|
This package also owns the **shared async REST client** (`tripl_cli.client`) and
|
|
44
44
|
the **shared request layer** above it (`tripl_cli.api`): every REST path, query
|
|
@@ -72,7 +72,7 @@ From git without a checkout — how you get a revision that is not released yet.
|
|
|
72
72
|
It resolves, but it is not exercised by CI:
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
|
-
uvx --from "git+https://github.com/
|
|
75
|
+
uvx --from "git+https://github.com/tripl-io/tripl.git#subdirectory=cli" tripl --version
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
## Commands
|
|
@@ -386,7 +386,7 @@ release. `title`, `summary` and `message` are prose. **Assert on `code` and
|
|
|
386
386
|
Full reference — every check, every finding code with its `evidence` keys, every
|
|
387
387
|
`watch` event token and the JSON Lines envelope, and what an operator should
|
|
388
388
|
actually do about each one:
|
|
389
|
-
<https://
|
|
389
|
+
<https://docs.tripl.io/run/cli> (source:
|
|
390
390
|
[`website/docs/run/cli.md`](../website/docs/run/cli.md)).
|
|
391
391
|
|
|
392
392
|
## Configuration
|
|
@@ -10,7 +10,7 @@ import package is **`tripl_cli`**. The last one is deliberate: the service's own
|
|
|
10
10
|
source package is `backend/src/tripl/`, so a distribution that installed an
|
|
11
11
|
importable `tripl` would shadow it in any environment holding both — a
|
|
12
12
|
contributor's backend venv, for one. The service is packaged as the separate
|
|
13
|
-
`tripl-server` distribution
|
|
13
|
+
`tripl-server` distribution, so `tripl` names this CLI alone.
|
|
14
14
|
|
|
15
15
|
This package also owns the **shared async REST client** (`tripl_cli.client`) and
|
|
16
16
|
the **shared request layer** above it (`tripl_cli.api`): every REST path, query
|
|
@@ -44,7 +44,7 @@ From git without a checkout — how you get a revision that is not released yet.
|
|
|
44
44
|
It resolves, but it is not exercised by CI:
|
|
45
45
|
|
|
46
46
|
```bash
|
|
47
|
-
uvx --from "git+https://github.com/
|
|
47
|
+
uvx --from "git+https://github.com/tripl-io/tripl.git#subdirectory=cli" tripl --version
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
## Commands
|
|
@@ -358,7 +358,7 @@ release. `title`, `summary` and `message` are prose. **Assert on `code` and
|
|
|
358
358
|
Full reference — every check, every finding code with its `evidence` keys, every
|
|
359
359
|
`watch` event token and the JSON Lines envelope, and what an operator should
|
|
360
360
|
actually do about each one:
|
|
361
|
-
<https://
|
|
361
|
+
<https://docs.tripl.io/run/cli> (source:
|
|
362
362
|
[`website/docs/run/cli.md`](../website/docs/run/cli.md)).
|
|
363
363
|
|
|
364
364
|
## Configuration
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tripl"
|
|
3
|
-
# 0.2.
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
version = "0.
|
|
3
|
+
# 0.2.3: the installer's compose file runs RabbitMQ's healthcheck as the
|
|
4
|
+
# rabbitmq user (gosu), so a first `tripl install` no longer races the Erlang
|
|
5
|
+
# cookie. Versioned with the service release; tripl-mcp is unchanged.
|
|
6
|
+
version = "0.3.0"
|
|
7
7
|
description = "Operator CLI for a tripl tracking-plan instance: diagnostics, health and live monitoring"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
requires-python = ">=3.12"
|
|
@@ -37,7 +37,7 @@ classifiers = [
|
|
|
37
37
|
]
|
|
38
38
|
dependencies = [
|
|
39
39
|
# httpx alone, and that is a BUDGET rather than a starting point: tripl-mcp
|
|
40
|
-
# imports its HTTP client from this distribution
|
|
40
|
+
# imports its HTTP client from this distribution, so every
|
|
41
41
|
# dependency added here is forced onto every MCP-server install too. Config
|
|
42
42
|
# parsing uses stdlib tomllib and argument parsing stdlib argparse for
|
|
43
43
|
# exactly that reason — no click, no rich, no tomli.
|
|
@@ -48,14 +48,14 @@ dependencies = [
|
|
|
48
48
|
]
|
|
49
49
|
|
|
50
50
|
[project.urls]
|
|
51
|
-
Homepage = "https://
|
|
51
|
+
Homepage = "https://docs.tripl.io/"
|
|
52
52
|
# The docs site sets `routeBasePath: '/'` under baseUrl `/tripl/`, so a page at
|
|
53
53
|
# website/docs/run/cli.md is served at /tripl/run/cli — there is no /docs/
|
|
54
54
|
# segment. mcp-server/pyproject.toml carried the /docs/ form and 404ed; both are
|
|
55
55
|
# corrected now, so check this shape when adding a third package.
|
|
56
|
-
Documentation = "https://
|
|
57
|
-
Repository = "https://github.com/
|
|
58
|
-
Issues = "https://github.com/
|
|
56
|
+
Documentation = "https://docs.tripl.io/run/cli"
|
|
57
|
+
Repository = "https://github.com/tripl-io/tripl"
|
|
58
|
+
Issues = "https://github.com/tripl-io/tripl/issues"
|
|
59
59
|
|
|
60
60
|
[project.scripts]
|
|
61
61
|
# `main` returns an int rather than calling sys.exit itself: the generated
|
|
@@ -83,12 +83,12 @@ build-backend = "hatchling.build"
|
|
|
83
83
|
# The import package is tripl_cli, NOT tripl: the service's own source package
|
|
84
84
|
# is backend/src/tripl/, and a distribution installing an importable `tripl`
|
|
85
85
|
# would shadow it in any environment holding both — a contributor's backend
|
|
86
|
-
# venv, for one
|
|
86
|
+
# venv, for one.
|
|
87
87
|
#
|
|
88
88
|
# No `artifacts` entry is needed for src/tripl_cli/install/files/*: hatchling
|
|
89
89
|
# copies a listed package wholesale, so compose.yaml and rabbitmq.conf ship as
|
|
90
90
|
# package data already. Verified against a built wheel rather than assumed —
|
|
91
|
-
# `uv build --wheel` then unzip, and both files are there
|
|
91
|
+
# `uv build --wheel` then unzip, and both files are there. They
|
|
92
92
|
# are read through importlib.resources, which is why this matters at all.
|
|
93
93
|
# The same holds for src/tripl_cli/codegen/templates/*.mustache, the built-in
|
|
94
94
|
# `tripl codegen` templates (#262): package data by the same rule, read the same way.
|
|
@@ -100,7 +100,7 @@ testpaths = ["tests"]
|
|
|
100
100
|
# An un-awaited coroutine is a RuntimeWarning printed at garbage-collection time,
|
|
101
101
|
# which in a fan-out test reads as unrelated noise long after the bug. doctor
|
|
102
102
|
# builds its fan-outs from comprehensions over targets precisely so a coroutine
|
|
103
|
-
# is never reused or dropped; this makes the failure mode loud
|
|
103
|
+
# is never reused or dropped; this makes the failure mode loud.
|
|
104
104
|
filterwarnings = ["error::RuntimeWarning"]
|
|
105
105
|
|
|
106
106
|
[tool.ruff]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Two consumers, one implementation: the ``tripl`` console script defined here and
|
|
4
4
|
the separate ``tripl-mcp`` distribution, which depends on this one and imports
|
|
5
|
-
``tripl_cli.client`` rather than carrying its own copy
|
|
5
|
+
``tripl_cli.client`` rather than carrying its own copy. This repo
|
|
6
6
|
has grown the same defect four times over — query keys drifted four ways, the
|
|
7
7
|
event-name regex three, plus security headers and the signal significance gate —
|
|
8
8
|
so a second HTTP client was not on the table.
|
|
@@ -15,7 +15,7 @@ from importlib.metadata import PackageNotFoundError, version
|
|
|
15
15
|
# The DISTRIBUTION name, not the import package name: this is `tripl`, while the
|
|
16
16
|
# module you are reading is `tripl_cli`. version(__name__) would raise
|
|
17
17
|
# PackageNotFoundError forever — nothing publishes a distribution called
|
|
18
|
-
# tripl_cli
|
|
18
|
+
# tripl_cli.
|
|
19
19
|
DISTRIBUTION_NAME = "tripl"
|
|
20
20
|
|
|
21
21
|
try:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""The one home for REST request construction
|
|
1
|
+
"""The one home for REST request construction.
|
|
2
2
|
|
|
3
3
|
Shared by the ``tripl`` CLI commands and by ``tripl-mcp``'s tools. A path
|
|
4
4
|
template appears here and nowhere else; ``ApiRequest`` is constructed here and
|
|
@@ -27,13 +27,13 @@ half-follow.
|
|
|
27
27
|
The one import out of this package is ``tripl_cli.model``, for its untyped-JSON
|
|
28
28
|
helpers and ``JOBS_WINDOW``. Those are facts about the wire rather than about
|
|
29
29
|
doctor's verdict machinery, which is why that module now sits at the package
|
|
30
|
-
root instead of under ``diagnostics``
|
|
30
|
+
root instead of under ``diagnostics`` — this import used to read as
|
|
31
31
|
the request layer depending on the doctor.
|
|
32
32
|
|
|
33
33
|
``page_items``/``page_total`` are RE-EXPORTED from there, not redefined: the
|
|
34
34
|
``{items, total}`` envelope is an envelope shape, so it is part of what this
|
|
35
35
|
package is about, and ``tripl_cli.api`` is the whole import surface ``tripl_mcp``
|
|
36
|
-
has.
|
|
36
|
+
has. Previously that unwrapping was written out by hand at four call
|
|
37
37
|
sites — three tool bodies plus the CLI's own watch loop — and they had already
|
|
38
38
|
diverged from the definition: the shared reader drops a non-dict row where the
|
|
39
39
|
tools passed it through, reading the same routes. ``cli/tests/test_contract.py``
|
|
@@ -16,7 +16,7 @@ ME = "/auth/me"
|
|
|
16
16
|
# the instance it just started instead of a static blurb — "create the first
|
|
17
17
|
# account, it becomes the owner" versus "this instance already has accounts"
|
|
18
18
|
# versus "registration is closed, ask an owner for an invitation". The auth
|
|
19
|
-
# screen reads it for the same reason
|
|
19
|
+
# screen reads it for the same reason.
|
|
20
20
|
STATUS = "/auth/status"
|
|
21
21
|
|
|
22
22
|
ENDPOINTS: tuple[tuple[str, str], ...] = (("get", ME), ("get", STATUS))
|
|
@@ -60,7 +60,7 @@ def get_diff(slug: str, branch_id: str) -> ApiRequest:
|
|
|
60
60
|
|
|
61
61
|
``tripl plan`` does not expose it: that payload is not a list, so it does
|
|
62
62
|
not fit the one document shape the seven read verbs share, and
|
|
63
|
-
``behind_base`` is too load-bearing to smuggle into an ``items`` array
|
|
64
|
-
|
|
63
|
+
``behind_base`` is too load-bearing to smuggle into an ``items`` array.
|
|
64
|
+
``tripl-mcp``'s ``get_branch_diff`` calls this.
|
|
65
65
|
"""
|
|
66
66
|
return ApiRequest("GET", DIFF.format(slug=slug, branch_id=branch_id))
|
|
@@ -45,7 +45,7 @@ DISMISS_ACTIONS: tuple[str, ...] = ("false_positive", "snooze")
|
|
|
45
45
|
|
|
46
46
|
# `tripl drifts reopen`, a verb of its own rather than a flag on `dismiss`:
|
|
47
47
|
# dismissing and reopening move a drift in opposite directions, and
|
|
48
|
-
# `dismiss --reopen` would name the wrong one
|
|
48
|
+
# `dismiss --reopen` would name the wrong one.
|
|
49
49
|
REOPEN_ACTION = "reopen"
|
|
50
50
|
|
|
51
51
|
# Everything the CLI can put on the wire. `accept` is the one exclusion, and it
|
|
@@ -150,7 +150,7 @@ def field_count(event_type: JsonDict) -> int:
|
|
|
150
150
|
replace the array it strips. It lives here, beside ``scans.is_dispatchable``
|
|
151
151
|
and for the same reason: a derived operational fact must not be computed
|
|
152
152
|
twice. Both call sites read it now — the MCP's ``_event_type_summary`` spelled
|
|
153
|
-
it inline until
|
|
153
|
+
it inline until recently, which meant two places had to remember that
|
|
154
154
|
``field_definitions`` is nullable on the wire.
|
|
155
155
|
"""
|
|
156
156
|
return len(event_type.get("field_definitions") or [])
|
|
@@ -201,7 +201,7 @@ def plan_drift_targets(
|
|
|
201
201
|
The per-project counts are what let the caller record what each project
|
|
202
202
|
actually got. A split budget lands unevenly by construction, and one
|
|
203
203
|
instance-wide ratio names no project: "we did not look there" is only useful
|
|
204
|
-
when it says where
|
|
204
|
+
when it says where.
|
|
205
205
|
"""
|
|
206
206
|
totals: dict[str, int] = {}
|
|
207
207
|
per_project: list[list[tuple[str, str]]] = []
|
|
@@ -5,7 +5,7 @@ No projection for ``EventListItemResponse``. Its ``field_values`` and
|
|
|
5
5
|
rather than into a model's context window — so ``tripl events list --json``
|
|
6
6
|
carries a row verbatim, exactly as ``scans.list_jobs`` does. The MCP's
|
|
7
7
|
``EVENT_LIST_FIELDS`` is a statement about an AGENT's context budget and stays
|
|
8
|
-
where its one consumer is
|
|
8
|
+
where its one consumer is.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
@@ -73,7 +73,7 @@ def list_events(
|
|
|
73
73
|
# (`EventFieldValue.value ILIKE %...%`) - the same shape `meta_value` has,
|
|
74
74
|
# on the other half of an event's content. It shipped with the route in
|
|
75
75
|
# PR #78 and was never mirrored here, so no shell and no agent could ask
|
|
76
|
-
# "which events carry this value"
|
|
76
|
+
# "which events carry this value".
|
|
77
77
|
field_value: str | None = None,
|
|
78
78
|
meta_value: str | None = None,
|
|
79
79
|
event_type_id: str | None = None,
|
|
@@ -81,7 +81,7 @@ def list_events(
|
|
|
81
81
|
# Tri-state, exactly as the route declares it: True and False isolate the
|
|
82
82
|
# two halves and OMITTING it means either. Not a spelling of `status` - an
|
|
83
83
|
# event can be marked reviewed and still sit at status=in_review, which is
|
|
84
|
-
# why the route grew an axis of its own
|
|
84
|
+
# why the route grew an axis of its own.
|
|
85
85
|
reviewed: bool | None = None,
|
|
86
86
|
# Tri-state like `reviewed`, and for the same reason: True keeps only events
|
|
87
87
|
# whose discussion still has an unanswered thread, False only those with
|
|
@@ -81,7 +81,7 @@ def replay_metrics(slug: str, scan_id: str, body: JsonDict) -> ApiRequest:
|
|
|
81
81
|
``scans.py`` guards this route with ``deps.get_owner_user``, which rejects
|
|
82
82
|
ANY request carrying ``request.state.api_key_scope`` — and every valid API
|
|
83
83
|
key sets it, read or write. So a Bearer-token client cannot reach it at all
|
|
84
|
-
and a ``tripl scans replay`` would 403 every time
|
|
84
|
+
and a ``tripl scans replay`` would 403 every time. The builder
|
|
85
85
|
exists so the contract test still watches the route; see the follow-up bead
|
|
86
86
|
on whether an owner-role ``tk_w_`` key should be allowed through.
|
|
87
87
|
"""
|
|
@@ -138,7 +138,7 @@ def config_names(scans: JsonList) -> dict[str, str]:
|
|
|
138
138
|
AND a time column because doctor asks "is the scheduler working", and a
|
|
139
139
|
config with no interval has no dispatch history worth reading. Watch — and
|
|
140
140
|
``tripl scans run`` — ask "what is running right now", and a manual replay on
|
|
141
|
-
an unscheduled config is exactly what
|
|
141
|
+
an unscheduled config is exactly what the watch command was built for.
|
|
142
142
|
|
|
143
143
|
Kept as a name of its own because that exclusion is the fact worth writing
|
|
144
144
|
down; the extraction itself is ``model.names_by_id``, which the event-type
|
|
@@ -30,7 +30,7 @@ ENTITY_TYPES: tuple[str, ...] = (
|
|
|
30
30
|
|
|
31
31
|
# The route's own bounds: `q` is Query(min_length=1, max_length=500) and `limit`
|
|
32
32
|
# is Query(ge=1, le=100) with a server default of 20. Checked locally so a
|
|
33
|
-
# too-long phrase fails at parse time rather than as a 422
|
|
33
|
+
# too-long phrase fails at parse time rather than as a 422.
|
|
34
34
|
QUERY_MAX_LENGTH = 500
|
|
35
35
|
LIMIT_DEFAULT = 20
|
|
36
36
|
LIMIT_MAX = 100
|
|
@@ -64,7 +64,7 @@ def semantic_used(payload: Any) -> bool:
|
|
|
64
64
|
something different, so a consumer that cannot tell the two regimes apart
|
|
65
65
|
misreads every score. ``tripl plan search`` publishes it in the document's
|
|
66
66
|
``meta`` and ``tripl-mcp``'s ``search_plan`` in its envelope — one route, one
|
|
67
|
-
reader, rather than a ``.get`` per surface
|
|
67
|
+
reader, rather than a ``.get`` per surface.
|
|
68
68
|
|
|
69
69
|
Absent reads as False rather than as unknown: the backend declares
|
|
70
70
|
``SearchResponse.semantic_used: bool = False``, so a body without the key IS
|
|
@@ -84,7 +84,7 @@ def reported_truncated(payload: Any) -> bool | None:
|
|
|
84
84
|
|
|
85
85
|
The second envelope fact this route adds, and the one ``total`` cannot give:
|
|
86
86
|
``total`` is ``len(items)`` after the trim, so on a full page it equals
|
|
87
|
-
``limit`` whether or not anything was dropped
|
|
87
|
+
``limit`` whether or not anything was dropped. ``/search``
|
|
88
88
|
takes no ``offset``, so the answer to a True is a bigger ``limit``.
|
|
89
89
|
|
|
90
90
|
Absent is ``None`` rather than False HERE because the two surfaces want two
|
|
@@ -34,7 +34,7 @@ from tripl_cli.render import plural
|
|
|
34
34
|
SARIF_VERSION = "2.1.0"
|
|
35
35
|
SARIF_SCHEMA = "https://json.schemastore.org/sarif-2.1.0.json"
|
|
36
36
|
SARIF_ROOT_BASE = "%SRCROOT%"
|
|
37
|
-
INFORMATION_URI = "https://
|
|
37
|
+
INFORMATION_URI = "https://docs.tripl.io/run/cli"
|
|
38
38
|
|
|
39
39
|
# What each finding code means, for SARIF's rule table and the human legend.
|
|
40
40
|
# A code the validator returns that is not listed here still renders; it just
|
|
@@ -49,6 +49,7 @@ RULE_TEXT: dict[str, str] = {
|
|
|
49
49
|
"dynamic_value": "The value is only known at runtime, so it could not be checked.",
|
|
50
50
|
"too_dynamic": "The name has too many runtime parts to look up in the plan.",
|
|
51
51
|
"wrong_type": "A property's value is not of the type the plan gives it.",
|
|
52
|
+
"policy_violation": "The call breaks a plan rule the server adds (an organization policy).",
|
|
52
53
|
"oversize_value": "A value exceeded the validator's size limits and was sent as null.",
|
|
53
54
|
"no_verdict": "The validator returned no verdict for this item.",
|
|
54
55
|
}
|
|
@@ -128,7 +128,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
128
128
|
except KeyboardInterrupt:
|
|
129
129
|
# Reached without a real SIGINT by `tripl watch`, whose loop catches the
|
|
130
130
|
# interrupt only long enough to print its footer and then RE-RAISES so
|
|
131
|
-
# this stays the only place 130 is decided
|
|
131
|
+
# this stays the only place 130 is decided.
|
|
132
132
|
print(file=sys.stderr)
|
|
133
133
|
return EXIT_INTERRUPTED
|
|
134
134
|
|
|
@@ -4,7 +4,7 @@ Maps tripl's auth/validation failures to typed exceptions the caller can render
|
|
|
4
4
|
however it needs. Pure pass-through otherwise — no re-modeling of API responses.
|
|
5
5
|
|
|
6
6
|
Shared by two distributions: the ``tripl`` CLI it ships in, and ``tripl-mcp``,
|
|
7
|
-
which imports it from here rather than carrying a copy
|
|
7
|
+
which imports it from here rather than carrying a copy. Nothing
|
|
8
8
|
consumer-specific belongs in this module — no MCP vocabulary, no CLI flag names,
|
|
9
9
|
no ``print``. Errors carry structure (``status_code``, ``base_url``) and each
|
|
10
10
|
consumer words its own guidance around them.
|
|
@@ -25,7 +25,7 @@ DEFAULT_TIMEOUT_SECONDS = 30.0
|
|
|
25
25
|
# What this distribution calls itself on the wire. The backend does not read
|
|
26
26
|
# User-Agent, but it is the only thing an operator reading access logs has to
|
|
27
27
|
# tell CLI traffic from agent traffic — so it is a PARAMETER, not a constant,
|
|
28
|
-
# and tripl-mcp passes its own
|
|
28
|
+
# and tripl-mcp passes its own.
|
|
29
29
|
DEFAULT_USER_AGENT = f"tripl/{__version__}"
|
|
30
30
|
|
|
31
31
|
|
|
@@ -9,7 +9,7 @@ than in a module of their own because they exist for one purpose only: a bad
|
|
|
9
9
|
``--days``, ``--timeout``, ``--until`` or ``--to`` fails at parse time with
|
|
10
10
|
``EXIT_USAGE``, before a socket is opened or a container is pulled.
|
|
11
11
|
|
|
12
|
-
...and, since
|
|
12
|
+
...and, since they were shared, the three flags that are the same flag on every
|
|
13
13
|
command that carries them. ``--json``, ``--timeout`` and ``--project`` were
|
|
14
14
|
spelled out at each ``add_parser`` call, which by the sixth verb was five copies
|
|
15
15
|
of the timeout default and two private ``_add_timeout`` helpers in two command
|
|
@@ -82,7 +82,7 @@ def register_all(
|
|
|
82
82
|
# word; a command acting on a CLASS OF OBJECTS is `<plural-noun> <verb>`.
|
|
83
83
|
scans.register(subparsers, parent)
|
|
84
84
|
drifts.register(subparsers, parent)
|
|
85
|
-
# The read groups
|
|
85
|
+
# The read groups. `events` obeys the grammar above exactly.
|
|
86
86
|
# `plan` bends it — its verbs name KINDS (`plan types`, `plan variables`)
|
|
87
87
|
# rather than actions — and that is deliberate: the alternative was
|
|
88
88
|
# `event-types list`, `variables list`, `branches list` and `search`, four
|
|
@@ -92,7 +92,7 @@ def register_all(
|
|
|
92
92
|
events.register(subparsers, parent)
|
|
93
93
|
plan.register(subparsers, parent)
|
|
94
94
|
# One word each, by the same rule: they act on an instance as a whole — one
|
|
95
|
-
# that does not exist yet, or one being moved to a new tag
|
|
95
|
+
# that does not exist yet, or one being moved to a new tag.
|
|
96
96
|
install.register(subparsers, parent)
|
|
97
97
|
upgrade.register(subparsers, parent)
|
|
98
98
|
# One word as well: it records one fact about the project ("we deployed") from
|
|
@@ -184,7 +184,7 @@ def require_single_project(args: argparse.Namespace) -> str:
|
|
|
184
184
|
infer from a drift id, and every ``events``/``plan`` route is per project
|
|
185
185
|
with no instance-wide form at all. Lives beside the other argparse rules
|
|
186
186
|
rather than in ``_write.py``, where it started: four READ verbs now need it,
|
|
187
|
-
and "exactly one project" was never a write-safety rule
|
|
187
|
+
and "exactly one project" was never a write-safety rule.
|
|
188
188
|
"""
|
|
189
189
|
slugs: tuple[str, ...] = tuple(getattr(args, "project", None) or ())
|
|
190
190
|
if len(slugs) == 1:
|
|
@@ -4,7 +4,7 @@ Seven verbs read seven resources and print seven tables. Everything AROUND that
|
|
|
4
4
|
— resolving ``--branch``, opening a counting reader, stamping the run, choosing
|
|
5
5
|
which stream the human lines go to, and writing at most one JSON document — is
|
|
6
6
|
the same seven times, and was already spelled six times across ``scans.py`` and
|
|
7
|
-
``drifts.py`` before these landed. It is spelled once here
|
|
7
|
+
``drifts.py`` before these landed. It is spelled once here.
|
|
8
8
|
|
|
9
9
|
The ``--branch`` rule is the load-bearing part. Every plan read is answered from
|
|
10
10
|
ONE revision, and which one is decided by a query parameter that
|
|
@@ -11,7 +11,7 @@ two config names share a prefix is worse than one that refuses to start, and a
|
|
|
11
11
|
``--branch`` that silently guessed would answer plan questions about the wrong
|
|
12
12
|
revision — which is the same class of wrong answer, on a read, and quieter. So
|
|
13
13
|
a selector matching nothing lists the candidates and exits 2, and a selector
|
|
14
|
-
matching two names them both and exits 2
|
|
14
|
+
matching two names them both and exits 2.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
``doctor``, ``status`` and ``watch`` are all read-only and a ``tk_r_`` key
|
|
4
4
|
suffices for every one of them. ``scans run``, ``scans cancel``,
|
|
5
5
|
``drifts dismiss`` and ``drifts reopen`` change the instance, which is a new
|
|
6
|
-
category and needs rules rather than habits
|
|
6
|
+
category and needs rules rather than habits:
|
|
7
7
|
|
|
8
8
|
* THE SERVER IS THE AUTHORITY ON SCOPE. The ``tk_r_``/``tk_w_`` prefix is derived
|
|
9
9
|
in ``api_key_service.py`` from the scope's first letter and says NOTHING about
|
|
@@ -32,7 +32,7 @@ from tripl_cli.errors import TriplConfigError, TriplError
|
|
|
32
32
|
from tripl_cli.model import JsonDict
|
|
33
33
|
|
|
34
34
|
# ``require_single_project`` used to live here. It moved to ``commands`` in
|
|
35
|
-
#
|
|
35
|
+
# four read verbs need it now, and "this command acts on one
|
|
36
36
|
# project" is an argument rule rather than a write-safety one — leaving it here
|
|
37
37
|
# would have had `tripl plan fields` importing the write-safety module.
|
|
38
38
|
|
|
@@ -4,7 +4,7 @@ The command the 2026-07-28..31 incident needed: a scan config failing for days
|
|
|
4
4
|
behind a generic error, events frozen with no indication why, a stale ACCEPTED
|
|
5
5
|
schema drift that had silently deleted a field, and a retry backoff that looked
|
|
6
6
|
like a hang. All four are visible through read-only REST calls, and this is the
|
|
7
|
-
one invocation that surfaces them
|
|
7
|
+
one invocation that surfaces them.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
@@ -11,13 +11,13 @@ Two facts shape this whole module:
|
|
|
11
11
|
``schema_drift_service._apply_acceptance_to_plan``, which DELETES the
|
|
12
12
|
FieldDefinition — the exact damage ``doctor``'s ``schema_field_deleted_by_accept``
|
|
13
13
|
finding exists to report. The tool that reports it must not be the easiest way
|
|
14
|
-
to cause it. Accepting stays in the tripl UI
|
|
14
|
+
to cause it. Accepting stays in the tripl UI.
|
|
15
15
|
|
|
16
16
|
``reopen`` is a VERB, not a flag on ``dismiss``. The two move a drift in
|
|
17
17
|
opposite directions, so ``dismiss --reopen`` would read as its own opposite. It
|
|
18
18
|
prompts for a reason ``dismiss``'s prompt does not share: reopening DISCARDS the
|
|
19
19
|
resolution note and the resolver, so the record of who triaged this drift and
|
|
20
|
-
why is gone, and nothing in the API restores it
|
|
20
|
+
why is gone, and nothing in the API restores it.
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
from __future__ import annotations
|
|
@@ -310,7 +310,7 @@ def run_list(args: argparse.Namespace, config: Config) -> int:
|
|
|
310
310
|
types_by_slug = dict(zip(found, type_reads, strict=True))
|
|
311
311
|
# The SAME plan doctor uses, from the same function: two implementations
|
|
312
312
|
# of a budgeted fan-out is how "we did not look there" starts printing as
|
|
313
|
-
# "nothing there"
|
|
313
|
+
# "nothing there".
|
|
314
314
|
targets, examined, totals = event_types_api.plan_drift_targets(
|
|
315
315
|
{slug: types_by_slug[slug].value or [] for slug in found}, budget=budget
|
|
316
316
|
)
|
|
@@ -8,8 +8,7 @@ edit that lands on main is an edit to the live plan nobody reviewed. Reproducing
|
|
|
8
8
|
that gate from a shell (resolve a branch, refuse main, prompt, then read the
|
|
9
9
|
server's canonical-name warnings back and tell the operator their proposed name
|
|
10
10
|
was ignored) is a command surface of its own, and shipping the easy half first
|
|
11
|
-
would leave the CLI with a write easier to get wrong than the agent's
|
|
12
|
-
(tripl-3ixs).
|
|
11
|
+
would leave the CLI with a write easier to get wrong than the agent's.
|
|
13
12
|
|
|
14
13
|
Both verbs take exactly one ``--project``: every route here is per project and
|
|
15
14
|
there is no instance-wide form, so a repeated ``--project`` would be a fan-out
|
|
@@ -110,7 +109,7 @@ def _register_list(
|
|
|
110
109
|
metavar="TEXT",
|
|
111
110
|
# "substring", not "exact": the route compares with ILIKE '%value%', so
|
|
112
111
|
# `--meta-value TRIPL-4` also keeps TRIPL-412. This line claimed "exact
|
|
113
|
-
# match" until
|
|
112
|
+
# match" until the sibling --field-value next to it and
|
|
114
113
|
# the two would have described one ILIKE two different ways.
|
|
115
114
|
help="substring match on any meta value, e.g. a ticket key (case-insensitive)",
|
|
116
115
|
)
|
|
@@ -7,7 +7,7 @@ files that already match are left alone, files that differ are reported and
|
|
|
7
7
|
kept, and the two compose commands run anyway, because "make the running stack
|
|
8
8
|
match what is on disk" is the useful meaning of a re-run.
|
|
9
9
|
|
|
10
|
-
WHAT IT DELIBERATELY DOES NOT DO
|
|
10
|
+
WHAT IT DELIBERATELY DOES NOT DO:
|
|
11
11
|
|
|
12
12
|
* It does not create the owner account, connect a data source or run the first
|
|
13
13
|
scan. Two of those three are unreachable with an API key at all and the third
|
|
@@ -115,6 +115,30 @@ def register(
|
|
|
115
115
|
"tag in production so a re-run cannot move you"
|
|
116
116
|
),
|
|
117
117
|
)
|
|
118
|
+
parser.add_argument(
|
|
119
|
+
"--edition",
|
|
120
|
+
dest="edition",
|
|
121
|
+
choices=sorted(files.EDITION_IMAGES),
|
|
122
|
+
# None rather than "community", for the same provenance reason as
|
|
123
|
+
# --version: only a typed edition is reported back when .env keeps
|
|
124
|
+
# another image.
|
|
125
|
+
default=None,
|
|
126
|
+
help=(
|
|
127
|
+
"which image .env pins as TRIPL_IMAGE (default: community). The enterprise "
|
|
128
|
+
"image is private: run `docker login ghcr.io` with your subscription's "
|
|
129
|
+
"credentials first"
|
|
130
|
+
),
|
|
131
|
+
)
|
|
132
|
+
parser.add_argument(
|
|
133
|
+
"--telemetry",
|
|
134
|
+
dest="telemetry",
|
|
135
|
+
action="store_true",
|
|
136
|
+
help=(
|
|
137
|
+
"share anonymous usage telemetry: one small ping a day, no names, emails, hosts "
|
|
138
|
+
"or data (TELEMETRY_ENABLED in the new .env; default: off). Asked once, here: an "
|
|
139
|
+
"existing .env is never rewritten"
|
|
140
|
+
),
|
|
141
|
+
)
|
|
118
142
|
add_wait_flag(parser)
|
|
119
143
|
parser.add_argument(
|
|
120
144
|
"--no-start",
|
|
@@ -247,7 +271,7 @@ def refuse_loose_append(appending: FileWrite | None) -> None:
|
|
|
247
271
|
secret is disclosed the moment it lands, and no later ``chmod`` un-discloses
|
|
248
272
|
it. Refusing rather than tightening the mode ourselves keeps rule 2 of
|
|
249
273
|
``install/files.py`` intact - the file is the operator's - and costs them one
|
|
250
|
-
``chmod`` they can see
|
|
274
|
+
``chmod`` they can see.
|
|
251
275
|
"""
|
|
252
276
|
if appending is None or not appending.secret:
|
|
253
277
|
return
|
|
@@ -338,16 +362,20 @@ def run_install(args: argparse.Namespace, config: Config) -> int:
|
|
|
338
362
|
refuse_source_checkout(directory, "install")
|
|
339
363
|
requested_url = normalize_base_url(str(args.app_url), FLAG_APP_URL)
|
|
340
364
|
requested_version = files.DEFAULT_VERSION if args.version is None else str(args.version)
|
|
365
|
+
requested_image = (
|
|
366
|
+
files.DEFAULT_IMAGE if args.edition is None else files.EDITION_IMAGES[str(args.edition)]
|
|
367
|
+
)
|
|
341
368
|
|
|
342
369
|
values = secrets.generate(secrets.default_rng())
|
|
343
370
|
writes = files.plan_writes(
|
|
344
371
|
directory,
|
|
345
372
|
app_base_url=requested_url,
|
|
346
|
-
image=
|
|
373
|
+
image=requested_image,
|
|
347
374
|
version=requested_version,
|
|
348
375
|
generated_at=generated_at,
|
|
349
376
|
values=values,
|
|
350
377
|
force=bool(args.force),
|
|
378
|
+
telemetry=bool(args.telemetry),
|
|
351
379
|
)
|
|
352
380
|
# What will be TRUE when this finishes, which on a re-run is the existing
|
|
353
381
|
# .env's values - that file is never overwritten. Everything downstream (the
|
|
@@ -356,11 +384,15 @@ def run_install(args: argparse.Namespace, config: Config) -> int:
|
|
|
356
384
|
settings = files.plan_settings(
|
|
357
385
|
directory,
|
|
358
386
|
app_base_url=requested_url,
|
|
359
|
-
image=
|
|
387
|
+
image=requested_image,
|
|
360
388
|
version=requested_version,
|
|
361
|
-
# --app-url is required, so it is always a real request. --version
|
|
362
|
-
# one only when
|
|
363
|
-
explicit=(
|
|
389
|
+
# --app-url is required, so it is always a real request. --version and
|
|
390
|
+
# --edition (TRIPL_IMAGE) are one only when they were typed.
|
|
391
|
+
explicit=(
|
|
392
|
+
"APP_BASE_URL",
|
|
393
|
+
*((files.VERSION_KEY,) if args.version is not None else ()),
|
|
394
|
+
*(("TRIPL_IMAGE",) if args.edition is not None else ()),
|
|
395
|
+
),
|
|
364
396
|
)
|
|
365
397
|
effective = {setting.name: setting.effective for setting in settings}
|
|
366
398
|
app_base_url = effective["APP_BASE_URL"].rstrip("/")
|
|
@@ -398,6 +430,14 @@ def run_install(args: argparse.Namespace, config: Config) -> int:
|
|
|
398
430
|
"onto an image you have not read the notes for.",
|
|
399
431
|
file=sys.stderr,
|
|
400
432
|
)
|
|
433
|
+
if plan.image == files.ENTERPRISE_IMAGE:
|
|
434
|
+
# Not checked here: the CLI holds no registry credentials, and a pull
|
|
435
|
+
# without them fails in `docker compose pull` with Docker's own words.
|
|
436
|
+
print(
|
|
437
|
+
f"tripl: note: {files.ENTERPRISE_IMAGE} is private. Run `docker login ghcr.io` "
|
|
438
|
+
"with the credentials that came with your subscription before the pull.",
|
|
439
|
+
file=sys.stderr,
|
|
440
|
+
)
|
|
401
441
|
if plan.insecure_scheme:
|
|
402
442
|
# Loud, and not a refusal: an operator terminating TLS elsewhere or
|
|
403
443
|
# evaluating on a laptop has a legitimate http:// origin.
|
|
@@ -467,7 +507,7 @@ def run_install(args: argparse.Namespace, config: Config) -> int:
|
|
|
467
507
|
# brought up in front of :8000 - so reading /auth/status here
|
|
468
508
|
# bought a ten-second stall against a URL the operator has just
|
|
469
509
|
# told us not to try, and then printed a "could not be read"
|
|
470
|
-
# line about an attempt nobody asked for
|
|
510
|
+
# line about an attempt nobody asked for.
|
|
471
511
|
print(file=human)
|
|
472
512
|
print(render_next_steps(None, app_base_url, probed=False), file=human)
|
|
473
513
|
elif health.ok:
|
|
@@ -13,8 +13,7 @@ an action. The alternative was one top-level group per REST collection —
|
|
|
13
13
|
is four more entries in ``tripl --help`` for four reads, and that is the same
|
|
14
14
|
objection that rejected ``list-scans``. An operator does not arrive with "I want
|
|
15
15
|
to query the event-types collection"; they arrive with "what does this plan look
|
|
16
|
-
like", and this is one group per QUESTION rather than one per collection
|
|
17
|
-
(tripl-3ixs).
|
|
16
|
+
like", and this is one group per QUESTION rather than one per collection.
|
|
18
17
|
|
|
19
18
|
``plan diff`` is deliberately ABSENT even though ``api.branches.get_diff``
|
|
20
19
|
exists and the MCP exposes it. The diff route answers
|
|
@@ -431,7 +430,7 @@ def run_search(args: argparse.Namespace, config: Config) -> int:
|
|
|
431
430
|
meta={"semantic_used": search_api.semantic_used(payload)},
|
|
432
431
|
)
|
|
433
432
|
# The route's OWN truncation flag, which `PlanRead.truncated` prefers to
|
|
434
|
-
# its page-fullness guess
|
|
433
|
+
# its page-fullness guess. That guess reads True whenever
|
|
435
434
|
# the matches land exactly on --limit, so this command was printing "more
|
|
436
435
|
# may have matched" on the same HTTP body that said nothing was dropped —
|
|
437
436
|
# and answering the opposite of `tripl-mcp`'s `search_plan`, which reads
|
|
@@ -11,7 +11,7 @@ more top-level entries, and no answer for the sixth.
|
|
|
11
11
|
``request.state.api_key_scope`` is set — and that is every valid API key, read or
|
|
12
12
|
write. A Bearer-token client cannot reach it, so the command would 403 every
|
|
13
13
|
time. ``scans cancel`` ships in its place: editor-gated, the natural pair to
|
|
14
|
-
``run``, and what the 2026-07-28..31 incident actually needed
|
|
14
|
+
``run``, and what the 2026-07-28..31 incident actually needed.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|