tripl 0.2.0__tar.gz → 0.2.2__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.0 → tripl-0.2.2}/PKG-INFO +130 -8
- {tripl-0.2.0 → tripl-0.2.2}/README.md +128 -6
- {tripl-0.2.0 → tripl-0.2.2}/pyproject.toml +6 -5
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/__init__.py +8 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/branches.py +10 -8
- tripl-0.2.2/src/tripl_cli/api/chart_annotations.py +73 -0
- tripl-0.2.2/src/tripl_cli/api/docs.py +191 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/endpoints.py +8 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/events.py +52 -0
- tripl-0.2.2/src/tripl_cli/api/plan_export.py +39 -0
- tripl-0.2.2/src/tripl_cli/api/plan_validation.py +78 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/search.py +43 -1
- tripl-0.2.2/src/tripl_cli/check/__init__.py +18 -0
- tripl-0.2.2/src/tripl_cli/check/calls.py +359 -0
- tripl-0.2.2/src/tripl_cli/check/config.py +868 -0
- tripl-0.2.2/src/tripl_cli/check/lexer.py +617 -0
- tripl-0.2.2/src/tripl_cli/check/model.py +121 -0
- tripl-0.2.2/src/tripl_cli/check/payloads.py +231 -0
- tripl-0.2.2/src/tripl_cli/check/presets.py +178 -0
- tripl-0.2.2/src/tripl_cli/check/render.py +253 -0
- tripl-0.2.2/src/tripl_cli/check/scan.py +367 -0
- tripl-0.2.2/src/tripl_cli/check/symbols.py +611 -0
- tripl-0.2.2/src/tripl_cli/check/validate.py +191 -0
- tripl-0.2.2/src/tripl_cli/check/values.py +393 -0
- tripl-0.2.2/src/tripl_cli/check/yamlish.py +404 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/cli.py +18 -3
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/client.py +7 -0
- tripl-0.2.2/src/tripl_cli/codegen/__init__.py +10 -0
- tripl-0.2.2/src/tripl_cli/codegen/calls.py +183 -0
- tripl-0.2.2/src/tripl_cli/codegen/context.py +530 -0
- tripl-0.2.2/src/tripl_cli/codegen/context_named.py +428 -0
- tripl-0.2.2/src/tripl_cli/codegen/context_plan.py +238 -0
- tripl-0.2.2/src/tripl_cli/codegen/files.py +103 -0
- tripl-0.2.2/src/tripl_cli/codegen/generate.py +351 -0
- tripl-0.2.2/src/tripl_cli/codegen/languages.py +185 -0
- tripl-0.2.2/src/tripl_cli/codegen/model.py +278 -0
- tripl-0.2.2/src/tripl_cli/codegen/naming.py +455 -0
- tripl-0.2.2/src/tripl_cli/codegen/template.py +205 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/named.kotlin.mustache +62 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/named.swift.mustache +138 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/named.ts.mustache +72 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/screen_view.kotlin.mustache +45 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/screen_view.swift.mustache +33 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/screen_view.ts.mustache +40 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/self_describing.kotlin.mustache +66 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/self_describing.swift.mustache +59 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/self_describing.ts.mustache +84 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/structured.kotlin.mustache +45 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/structured.swift.mustache +33 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/structured.ts.mustache +40 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/transport.kotlin.mustache +30 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/transport.swift.mustache +33 -0
- tripl-0.2.2/src/tripl_cli/codegen/templates/transport.ts.mustache +57 -0
- tripl-0.2.2/src/tripl_cli/codegen/value_types.py +145 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/__init__.py +22 -0
- tripl-0.2.2/src/tripl_cli/commands/_docs_files.py +215 -0
- tripl-0.2.2/src/tripl_cli/commands/annotate.py +225 -0
- tripl-0.2.2/src/tripl_cli/commands/check.py +221 -0
- tripl-0.2.2/src/tripl_cli/commands/codegen.py +315 -0
- tripl-0.2.2/src/tripl_cli/commands/docs.py +537 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/events.py +97 -2
- tripl-0.2.2/src/tripl_cli/commands/export.py +183 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/install.py +2 -2
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/plan.py +28 -14
- tripl-0.2.2/src/tripl_cli/commands/whoami.py +93 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/diagnostics/collect.py +9 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/diagnostics/endpoints.py +43 -4
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/diagnostics/scan_checks.py +18 -21
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/install/files/compose.yaml +67 -1
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/install/files.py +3 -2
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/model.py +92 -14
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/render.py +70 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/report.py +142 -1
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/watch/collect.py +1 -1
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/watch/model.py +1 -1
- tripl-0.2.2/tests/codegen/check.yml +30 -0
- tripl-0.2.2/tests/codegen/custom/check.yml +57 -0
- tripl-0.2.2/tests/codegen/custom/golden/kotlin/AcmeAction.kt +14 -0
- tripl-0.2.2/tests/codegen/custom/golden/kotlin/AcmeAppConstants.kt +21 -0
- tripl-0.2.2/tests/codegen/custom/golden/kotlin/AcmeCategory.kt +12 -0
- tripl-0.2.2/tests/codegen/custom/golden/kotlin/AcmeLabel.kt +16 -0
- tripl-0.2.2/tests/codegen/custom/golden/kotlin/AcmeProperty.kt +10 -0
- tripl-0.2.2/tests/codegen/custom/golden/kotlin/AcmeScreens.kt +21 -0
- tripl-0.2.2/tests/codegen/custom/golden/kotlin/AcmeStructured.kt +25 -0
- tripl-0.2.2/tests/codegen/custom/golden/kotlin/TriplTransport.kt +30 -0
- tripl-0.2.2/tests/codegen/custom/golden/swift/AcmeAppConstants.swift +19 -0
- tripl-0.2.2/tests/codegen/custom/golden/swift/AcmePlanned.swift +18 -0
- tripl-0.2.2/tests/codegen/custom/golden/swift/AcmeScreens.swift +41 -0
- tripl-0.2.2/tests/codegen/custom/golden/swift/AcmeStructured.swift +54 -0
- tripl-0.2.2/tests/codegen/custom/golden/swift/TriplTransport.swift +35 -0
- tripl-0.2.2/tests/codegen/custom/golden/ts/acmeAction.ts +13 -0
- tripl-0.2.2/tests/codegen/custom/golden/ts/acmeAppConstants.ts +19 -0
- tripl-0.2.2/tests/codegen/custom/golden/ts/acmeCategory.ts +11 -0
- tripl-0.2.2/tests/codegen/custom/golden/ts/acmeLabel.ts +15 -0
- tripl-0.2.2/tests/codegen/custom/golden/ts/acmeProperty.ts +9 -0
- tripl-0.2.2/tests/codegen/custom/golden/ts/acmeScreens.ts +20 -0
- tripl-0.2.2/tests/codegen/custom/golden/ts/acmeStructured.ts +20 -0
- tripl-0.2.2/tests/codegen/custom/golden/ts/triplTransport.ts +59 -0
- tripl-0.2.2/tests/codegen/custom/model.json +65 -0
- tripl-0.2.2/tests/codegen/custom/templates/holder.kotlin.mustache +12 -0
- tripl-0.2.2/tests/codegen/custom/templates/holder.ts.mustache +9 -0
- tripl-0.2.2/tests/codegen/custom/templates/named.kotlin.mustache +27 -0
- tripl-0.2.2/tests/codegen/custom/templates/named.swift.mustache +23 -0
- tripl-0.2.2/tests/codegen/custom/templates/named.ts.mustache +23 -0
- tripl-0.2.2/tests/codegen/custom/templates/planned.swift.mustache +11 -0
- tripl-0.2.2/tests/codegen/custom/templates/screen.kotlin.mustache +18 -0
- tripl-0.2.2/tests/codegen/custom/templates/screen.swift.mustache +30 -0
- tripl-0.2.2/tests/codegen/custom/templates/screen.ts.mustache +17 -0
- tripl-0.2.2/tests/codegen/custom/templates/structured.kotlin.mustache +21 -0
- tripl-0.2.2/tests/codegen/custom/templates/structured.swift.mustache +27 -0
- tripl-0.2.2/tests/codegen/custom/templates/structured.ts.mustache +19 -0
- tripl-0.2.2/tests/codegen/golden/kotlin/AppEvents.kt +43 -0
- tripl-0.2.2/tests/codegen/golden/kotlin/LegacyTracking.kt +79 -0
- tripl-0.2.2/tests/codegen/golden/kotlin/ScreenTracking.kt +36 -0
- tripl-0.2.2/tests/codegen/golden/kotlin/SdTracking.kt +57 -0
- tripl-0.2.2/tests/codegen/golden/kotlin/TriplTransport.kt +30 -0
- tripl-0.2.2/tests/codegen/golden/swift/AppEvents.swift +39 -0
- tripl-0.2.2/tests/codegen/golden/swift/LegacyTracking.swift +132 -0
- tripl-0.2.2/tests/codegen/golden/swift/ScreenTracking.swift +32 -0
- tripl-0.2.2/tests/codegen/golden/swift/SdTracking.swift +79 -0
- tripl-0.2.2/tests/codegen/golden/swift/TriplTransport.swift +35 -0
- tripl-0.2.2/tests/codegen/golden/ts/appEvents.ts +43 -0
- tripl-0.2.2/tests/codegen/golden/ts/legacyTracking.ts +83 -0
- tripl-0.2.2/tests/codegen/golden/ts/screenTracking.ts +36 -0
- tripl-0.2.2/tests/codegen/golden/ts/sdTracking.ts +69 -0
- tripl-0.2.2/tests/codegen/golden/ts/triplTransport.ts +59 -0
- tripl-0.2.2/tests/codegen/model.json +93 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/conftest.py +64 -3
- tripl-0.2.2/tests/test_annotate_cmd.py +298 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_api_requests.py +97 -1
- tripl-0.2.2/tests/test_check_cmd.py +640 -0
- tripl-0.2.2/tests/test_check_scan.py +639 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_checks.py +10 -4
- tripl-0.2.2/tests/test_codegen_cmd.py +305 -0
- tripl-0.2.2/tests/test_codegen_custom.py +653 -0
- tripl-0.2.2/tests/test_codegen_generate.py +617 -0
- tripl-0.2.2/tests/test_codegen_naming.py +128 -0
- tripl-0.2.2/tests/test_codegen_template.py +83 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_contract.py +307 -19
- tripl-0.2.2/tests/test_docs_cmd.py +431 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_events_cmd.py +112 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_output_samples.py +43 -6
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_plan_cmd.py +72 -5
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_watch.py +2 -2
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_watch_diff.py +1 -1
- tripl-0.2.2/tests/test_whoami_cmd.py +99 -0
- {tripl-0.2.0 → tripl-0.2.2}/uv.lock +1 -1
- {tripl-0.2.0 → tripl-0.2.2}/.gitignore +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/LICENSE +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/__init__.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/__main__.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/auth.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/data_sources.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/event_types.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/monitoring.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/projects.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/request.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/scans.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/api/variables.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/_plan.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/_resolve.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/_write.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/doctor.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/drifts.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/scans.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/status.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/upgrade.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/commands/watch.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/config.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/diagnostics/__init__.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/diagnostics/checks.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/errors.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/install/__init__.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/install/docker.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/install/files/rabbitmq.conf +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/install/health.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/install/plan.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/install/render.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/install/secrets.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/install/shell.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/py.typed +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/runner.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/watch/__init__.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/watch/diff.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/watch/loop.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/src/tripl_cli/watch/render.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/__init__.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_cli.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_client.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_config.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_docker_detect.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_doctor.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_drifts_cmd.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_install_cmd.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_install_files.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_runner.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_scans_cmd.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_status.py +0 -0
- {tripl-0.2.0 → tripl-0.2.2}/tests/test_upgrade_cmd.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: tripl
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Operator CLI for a tripl tracking-plan instance: diagnostics, health and live monitoring
|
|
5
5
|
Project-URL: Homepage, https://vladenisov.github.io/tripl/
|
|
6
6
|
Project-URL: Documentation, https://vladenisov.github.io/tripl/run/cli
|
|
@@ -98,6 +98,13 @@ tripl scans cancel <scan> <job-id> --project SLUG # cancel an active job (WRIT
|
|
|
98
98
|
tripl drifts list # schema drifts; untriaged by default
|
|
99
99
|
tripl drifts dismiss <drift-id> --project SLUG # false_positive or snooze (WRITE)
|
|
100
100
|
tripl drifts reopen <drift-id> --project SLUG # back to open; drops the note (WRITE)
|
|
101
|
+
tripl annotate "Deployed web 2026.09.25" --project SLUG --url URL # deploy marker on monitoring charts (WRITE)
|
|
102
|
+
tripl check # validate the tracking calls in this checkout against the plan
|
|
103
|
+
tripl check --payloads events.ndjson # validate captured events; a missing required field fails
|
|
104
|
+
tripl check --format sarif > tripl.sarif # SARIF 2.1.0 for code scanning
|
|
105
|
+
tripl codegen # typed tracking code (Swift, Kotlin, TypeScript) from the plan
|
|
106
|
+
tripl codegen --check # CI: exit 1 when the committed generated files are out of date
|
|
107
|
+
tripl export --out plan-schemas # one JSON Schema (2020-12) per event, plus the bundle
|
|
101
108
|
tripl install --app-url https://tripl.example.com --version 1.5.0 # provision a stack and start it (HOST)
|
|
102
109
|
tripl install --app-url https://tripl.example.com --dry-run # print the plan, write nothing
|
|
103
110
|
tripl upgrade --to 1.6.0 # move an installed stack to a new image tag (HOST)
|
|
@@ -108,7 +115,7 @@ cannot move you onto an image you have not read the notes for. The command says
|
|
|
108
115
|
so on stderr when you leave it at the default.
|
|
109
116
|
|
|
110
117
|
`doctor`, `status`, `watch`, `scans list`, `scans jobs` and `drifts list` are
|
|
111
|
-
**read-only** — a `tk_r_` key is enough. The
|
|
118
|
+
**read-only** — a `tk_r_` key is enough. The five marked WRITE need a `tk_w_`
|
|
112
119
|
key backed by an editor or owner, and the CLI does not pre-judge that: the key
|
|
113
120
|
prefix is derived from the scope's first letter server-side and says nothing
|
|
114
121
|
about the user's role, so the request is sent and the API's own 403 is printed.
|
|
@@ -116,12 +123,122 @@ about the user's role, so the request is sent and the API's own 403 is printed.
|
|
|
116
123
|
`scans cancel`, `drifts dismiss` and `drifts reopen` prompt on a terminal and
|
|
117
124
|
take `--yes`; when stdin is **not** a terminal and `--yes` was not given they
|
|
118
125
|
refuse with exit 2 rather than hanging a cron job or proceeding silently.
|
|
119
|
-
`scans run`
|
|
120
|
-
because a no-op flag here is a flag a script author will assume works
|
|
121
|
-
command too. All
|
|
126
|
+
`scans run` and `annotate` do not prompt and have no `--yes` at all — passing one
|
|
127
|
+
is exit 2, because a no-op flag here is a flag a script author will assume works
|
|
128
|
+
on the next command too. All five writes take `--dry-run`, which resolves everything, prints
|
|
122
129
|
the exact request (method, path, params, body — never a credential) and sends
|
|
123
130
|
nothing.
|
|
124
131
|
|
|
132
|
+
`annotate` posts a chart marker with source `api`, for a deploy step in CI. On
|
|
133
|
+
this one command `--url` is the release link the marker opens, not the instance:
|
|
134
|
+
give the instance with `TRIPL_BASE_URL`, `--base-url`, or `--url` before the
|
|
135
|
+
command name. The API de-duplicates the same `api` label within 24 hours (manual
|
|
136
|
+
annotations are never de-duplicated) and answers
|
|
137
|
+
200 with the existing marker; `annotate` says so and still exits 0, so a retried
|
|
138
|
+
job is harmless.
|
|
139
|
+
|
|
140
|
+
`check` validates code against the plan. `.tripl/check.yml` (found at or above
|
|
141
|
+
the current directory, up to the repository root; `--check-config PATH`
|
|
142
|
+
otherwise) names the project and, **per event type**, how its calls look — your
|
|
143
|
+
own wrapper first, SDK presets as shorthands:
|
|
144
|
+
|
|
145
|
+
```yaml
|
|
146
|
+
project: my-app
|
|
147
|
+
sources: ["Sources/**", "web/src/**"]
|
|
148
|
+
enums: [{file: "Sources/**/Events.swift", languages: [swift]}]
|
|
149
|
+
event_types:
|
|
150
|
+
se:
|
|
151
|
+
calls:
|
|
152
|
+
- function: "Analytics.shared.log" # or `pattern:` (a regex)
|
|
153
|
+
args: {category: category, action: action, label: label, properties: properties}
|
|
154
|
+
- objc_selector: "trackWithCategory:action:label:"
|
|
155
|
+
page: {preset: snowplow_screen_view}
|
|
156
|
+
legacy: {calls: [{function: "Analytics.shared.logEvent", name_arg: 0}]}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Presets: `segment_track`, `amplitude_log_event`, `snowplow_structured`,
|
|
160
|
+
`snowplow_screen_view`, `snowplow_self_describing`. Swift, Objective-C, Kotlin,
|
|
161
|
+
Java and TypeScript/JavaScript are read; enum shorthand (`.home`) and qualified
|
|
162
|
+
cases resolve through the enum files, interpolated names become plan variables
|
|
163
|
+
(`"promo_sheet_\(id)_shown"` is `promo_sheet_${id}_shown`), Kotlin/Java
|
|
164
|
+
`.name` / `name()` and Swift `.description` read an enum case's own name, and a
|
|
165
|
+
value only known at runtime is sent as unknown, never as an error (`--strict`
|
|
166
|
+
reports it). `--payloads` validates captured events instead, where a missing
|
|
167
|
+
required field IS an error; a value over the validator's size limits (name 500,
|
|
168
|
+
event type 100, field value 2000 characters, 200 fields) is sent as unknown and
|
|
169
|
+
flagged as an `oversize_value` warning on its line.
|
|
170
|
+
`check` reads nothing but the plan: any member's key works, `tk_r_` included.
|
|
171
|
+
|
|
172
|
+
`codegen` turns the plan into typed tracking code, from the same
|
|
173
|
+
`.tripl/check.yml`. It generates **per event-type style, never a function per
|
|
174
|
+
event**, and the generated code calls **your own wrapper** (the `transport`) —
|
|
175
|
+
or, with none configured, the shared `TriplDestination` in
|
|
176
|
+
`TriplTransport.swift` / `.kt` / `triplTransport.ts` that you implement once.
|
|
177
|
+
It never imports an SDK.
|
|
178
|
+
|
|
179
|
+
| `style` | What is generated |
|
|
180
|
+
|---------|-------------------|
|
|
181
|
+
| `structured` | an enum per plan field (its cases are the plan's values; a variable-backed value contributes its allowed values; free text stays `String`), your wrapper's call with its argument types narrowed — `log(category: Category, action: Action, label: String, properties:)` — and a compiler-checked `knownEvents` list |
|
|
182
|
+
| `screen_view` | the same, with `ScreenType` / `ScreenId` enums |
|
|
183
|
+
| `named` | one generic `track(event)`: Swift `enum LegacyEvent { case homeScreenView(HomeScreenView) … }` with `name` and `properties`; Kotlin a sealed interface of data classes/objects; TypeScript `track<K extends LegacyEventName>(name: K, props: LegacyEventProps[K])` |
|
|
184
|
+
| `self_describing` | one data class per schema with its fields, sharing one `track` |
|
|
185
|
+
|
|
186
|
+
```yaml
|
|
187
|
+
codegen:
|
|
188
|
+
out: {swift: Sources/Tracking/Generated, kotlin: app/src/main/java/tracking, ts: web/src/tracking}
|
|
189
|
+
kotlin_package: com.example.tracking
|
|
190
|
+
event_types:
|
|
191
|
+
se:
|
|
192
|
+
calls: [{function: "Analytics.shared.log", args: {category: category, action: action, label: label, properties: properties}}]
|
|
193
|
+
codegen:
|
|
194
|
+
style: structured # default: from the preset, else structured when the type has a name rule
|
|
195
|
+
transport:
|
|
196
|
+
swift: "Analytics.shared.log" # reuses the `calls` entry's args mapping
|
|
197
|
+
ts: {function: "analytics.log", positional: [category, action, label, properties],
|
|
198
|
+
import: "import { analytics } from './analytics';"}
|
|
199
|
+
type_names: {namespace: AppEvents, category: EventCategory, function: log}
|
|
200
|
+
template: {swift: .tripl/templates/structured.swift.mustache} # optional override
|
|
201
|
+
legacy:
|
|
202
|
+
calls: [{function: "Analytics.shared.logEvent", name_arg: 0, properties_arg: parameters}]
|
|
203
|
+
codegen: {style: named, languages: [swift, kotlin]}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
A `transport` given as a bare function reuses the `args` / `positional` /
|
|
207
|
+
`object_arg` mapping of the `calls` entry with the same `function`. Swift passes
|
|
208
|
+
labelled arguments with their labels, Kotlin as named arguments (give
|
|
209
|
+
`positional` for a Java wrapper), TypeScript positionally — or as one object
|
|
210
|
+
literal with `object_arg`. `type_names` renames `namespace`, `event` (the named
|
|
211
|
+
/ self-describing event type), `function`, and any field's enum by field name.
|
|
212
|
+
|
|
213
|
+
An event's parameters are the fields it does not fix plus one per `${token}` in
|
|
214
|
+
its name (`promo_sheet_${sheet_id}_shown` takes `sheetId`, typed by the
|
|
215
|
+
variable's allowed values). Plan strings become identifiers by splitting on
|
|
216
|
+
anything that is not a letter or digit and on camelCase: `Home Screen View` ->
|
|
217
|
+
`homeScreenView` (`HOME_SCREEN_VIEW` for Kotlin enum entries), `checkout:start`
|
|
218
|
+
-> `checkoutStart`; a leading digit gets `_` (`_1stRun`), a reserved word a
|
|
219
|
+
trailing `_` (`default_`), a reserved type name `Value` (`TypeValue`), and a
|
|
220
|
+
collision `2`, `3` in sorted order. The raw plan string is always what is sent.
|
|
221
|
+
Deprecated events are marked (`@available(*, deprecated)`, `@Deprecated`,
|
|
222
|
+
`@deprecated`); archived ones are not generated.
|
|
223
|
+
|
|
224
|
+
A custom `template` uses the built-in one's context (copy it from
|
|
225
|
+
`tripl_cli/codegen/templates/`): a Mustache subset — `{{name}}`, `{{a.b}}`,
|
|
226
|
+
`{{#list}}…{{/list}}`, `{{^empty}}…{{/empty}}`, `{{! comment }}` — where every
|
|
227
|
+
plan string arrives already quoted or escaped for the language, and an unknown
|
|
228
|
+
`{{name}}` is an error rather than an empty string.
|
|
229
|
+
|
|
230
|
+
Output is deterministic (sorted, no timestamps) with a `Generated by tripl
|
|
231
|
+
codegen — do not edit` header naming the project, branch and the plan's
|
|
232
|
+
content hash (not its revision, so a new revision that changes nothing is not
|
|
233
|
+
drift), so it can be committed: `tripl codegen --check` writes nothing and exits
|
|
234
|
+
1 when a file is missing, differs, or is stale (generated earlier, no longer
|
|
235
|
+
produced — a normal run deletes those, and only files carrying the header for
|
|
236
|
+
the same project, in a language the run writes into that directory). `--model FILE`
|
|
237
|
+
generates from a saved `tripl export --format codegen_model` without an
|
|
238
|
+
instance. `export` writes `bundle.json` and `<event type>/<identity>.schema.json`
|
|
239
|
+
per event (file names sanitised to `[A-Za-z0-9._-]`), or prints the export to
|
|
240
|
+
stdout without `--out`. Both read nothing but the plan: any member's key works.
|
|
241
|
+
|
|
125
242
|
`drifts reopen` is the one whose prompt is worth reading: reopening clears the
|
|
126
243
|
drift's `resolution_note`, `resolved_by` and `resolved_at`, and dismissing it
|
|
127
244
|
again does not bring them back.
|
|
@@ -238,10 +355,15 @@ not moved).
|
|
|
238
355
|
|
|
239
356
|
| Exit | Meaning |
|
|
240
357
|
|------|---------|
|
|
241
|
-
| 0 | Every check passed, or only warned and `--strict` was not given. `status`, whenever it completed. `watch`, whenever the run completed — a failed job or a new signal is still 0. The `scans` / `drifts` verbs
|
|
358
|
+
| 0 | Every check passed, or only warned and `--strict` was not given. `status`, whenever it completed. `watch`, whenever the run completed — a failed job or a new signal is still 0. The `scans` / `drifts` verbs and `annotate`, whenever every read arrived or the write was accepted (`--dry-run` included, and a de-duplicated `annotate` too). |
|
|
242
359
|
| 1 | The tool itself broke (doctor turns every API failure into a finding), or any other command could not complete a request — unreachable, or the API refused it. For `watch` this includes a key revoked mid-run. For `scans list` / `drifts list` it includes **any** failed read in the fan-out; for `scans run`, a job returned already `failed`; for `scans cancel` / `drifts dismiss` / `drifts reopen`, a declined prompt. |
|
|
243
|
-
| 2 | Usage or configuration error. For `doctor` and `status` that is resolved before any socket opens; `watch` also refuses after reading the listings, when `--scan` matches nothing or more than 24 scan configs are selected. The `scans` / `drifts` verbs add a bare group, a missing or repeated `--project`, an unresolved or ambiguous `<scan>`, and a prompting write on a non-TTY without `--yes
|
|
360
|
+
| 2 | Usage or configuration error. For `doctor` and `status` that is resolved before any socket opens; `watch` also refuses after reading the listings, when `--scan` matches nothing or more than 24 scan configs are selected. The `scans` / `drifts` verbs add a bare group, a missing or repeated `--project`, an unresolved or ambiguous `<scan>`, and a prompting write on a non-TTY without `--yes`; `annotate` adds a `--url` that is not http(s), an `--at` that is not RFC 3339, and half a scope. Either way **no JSON is emitted** and no write is sent. |
|
|
244
361
|
| 3 | `doctor` only: at least one check failed, or `--strict` and at least one warning. Nothing else ever exits 3. |
|
|
362
|
+
|
|
363
|
+
`check` uses 0, 1 and 2: 1 when any call site or event has an error (or, with
|
|
364
|
+
`--strict`, a warning), and 2 for a bad check config or payload file.
|
|
365
|
+
`codegen` uses them too: 1 for drift under `--check` (or a plan without a
|
|
366
|
+
configured event type), 2 for a bad check config or template.
|
|
245
367
|
| 130 | Interrupted (SIGINT). For `watch` this is the **normal** ending — a run without `--duration` has no other way to stop. |
|
|
246
368
|
|
|
247
369
|
An unreachable instance therefore exits **3** out of `doctor`, not 1 — it
|
|
@@ -70,6 +70,13 @@ tripl scans cancel <scan> <job-id> --project SLUG # cancel an active job (WRIT
|
|
|
70
70
|
tripl drifts list # schema drifts; untriaged by default
|
|
71
71
|
tripl drifts dismiss <drift-id> --project SLUG # false_positive or snooze (WRITE)
|
|
72
72
|
tripl drifts reopen <drift-id> --project SLUG # back to open; drops the note (WRITE)
|
|
73
|
+
tripl annotate "Deployed web 2026.09.25" --project SLUG --url URL # deploy marker on monitoring charts (WRITE)
|
|
74
|
+
tripl check # validate the tracking calls in this checkout against the plan
|
|
75
|
+
tripl check --payloads events.ndjson # validate captured events; a missing required field fails
|
|
76
|
+
tripl check --format sarif > tripl.sarif # SARIF 2.1.0 for code scanning
|
|
77
|
+
tripl codegen # typed tracking code (Swift, Kotlin, TypeScript) from the plan
|
|
78
|
+
tripl codegen --check # CI: exit 1 when the committed generated files are out of date
|
|
79
|
+
tripl export --out plan-schemas # one JSON Schema (2020-12) per event, plus the bundle
|
|
73
80
|
tripl install --app-url https://tripl.example.com --version 1.5.0 # provision a stack and start it (HOST)
|
|
74
81
|
tripl install --app-url https://tripl.example.com --dry-run # print the plan, write nothing
|
|
75
82
|
tripl upgrade --to 1.6.0 # move an installed stack to a new image tag (HOST)
|
|
@@ -80,7 +87,7 @@ cannot move you onto an image you have not read the notes for. The command says
|
|
|
80
87
|
so on stderr when you leave it at the default.
|
|
81
88
|
|
|
82
89
|
`doctor`, `status`, `watch`, `scans list`, `scans jobs` and `drifts list` are
|
|
83
|
-
**read-only** — a `tk_r_` key is enough. The
|
|
90
|
+
**read-only** — a `tk_r_` key is enough. The five marked WRITE need a `tk_w_`
|
|
84
91
|
key backed by an editor or owner, and the CLI does not pre-judge that: the key
|
|
85
92
|
prefix is derived from the scope's first letter server-side and says nothing
|
|
86
93
|
about the user's role, so the request is sent and the API's own 403 is printed.
|
|
@@ -88,12 +95,122 @@ about the user's role, so the request is sent and the API's own 403 is printed.
|
|
|
88
95
|
`scans cancel`, `drifts dismiss` and `drifts reopen` prompt on a terminal and
|
|
89
96
|
take `--yes`; when stdin is **not** a terminal and `--yes` was not given they
|
|
90
97
|
refuse with exit 2 rather than hanging a cron job or proceeding silently.
|
|
91
|
-
`scans run`
|
|
92
|
-
because a no-op flag here is a flag a script author will assume works
|
|
93
|
-
command too. All
|
|
98
|
+
`scans run` and `annotate` do not prompt and have no `--yes` at all — passing one
|
|
99
|
+
is exit 2, because a no-op flag here is a flag a script author will assume works
|
|
100
|
+
on the next command too. All five writes take `--dry-run`, which resolves everything, prints
|
|
94
101
|
the exact request (method, path, params, body — never a credential) and sends
|
|
95
102
|
nothing.
|
|
96
103
|
|
|
104
|
+
`annotate` posts a chart marker with source `api`, for a deploy step in CI. On
|
|
105
|
+
this one command `--url` is the release link the marker opens, not the instance:
|
|
106
|
+
give the instance with `TRIPL_BASE_URL`, `--base-url`, or `--url` before the
|
|
107
|
+
command name. The API de-duplicates the same `api` label within 24 hours (manual
|
|
108
|
+
annotations are never de-duplicated) and answers
|
|
109
|
+
200 with the existing marker; `annotate` says so and still exits 0, so a retried
|
|
110
|
+
job is harmless.
|
|
111
|
+
|
|
112
|
+
`check` validates code against the plan. `.tripl/check.yml` (found at or above
|
|
113
|
+
the current directory, up to the repository root; `--check-config PATH`
|
|
114
|
+
otherwise) names the project and, **per event type**, how its calls look — your
|
|
115
|
+
own wrapper first, SDK presets as shorthands:
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
project: my-app
|
|
119
|
+
sources: ["Sources/**", "web/src/**"]
|
|
120
|
+
enums: [{file: "Sources/**/Events.swift", languages: [swift]}]
|
|
121
|
+
event_types:
|
|
122
|
+
se:
|
|
123
|
+
calls:
|
|
124
|
+
- function: "Analytics.shared.log" # or `pattern:` (a regex)
|
|
125
|
+
args: {category: category, action: action, label: label, properties: properties}
|
|
126
|
+
- objc_selector: "trackWithCategory:action:label:"
|
|
127
|
+
page: {preset: snowplow_screen_view}
|
|
128
|
+
legacy: {calls: [{function: "Analytics.shared.logEvent", name_arg: 0}]}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Presets: `segment_track`, `amplitude_log_event`, `snowplow_structured`,
|
|
132
|
+
`snowplow_screen_view`, `snowplow_self_describing`. Swift, Objective-C, Kotlin,
|
|
133
|
+
Java and TypeScript/JavaScript are read; enum shorthand (`.home`) and qualified
|
|
134
|
+
cases resolve through the enum files, interpolated names become plan variables
|
|
135
|
+
(`"promo_sheet_\(id)_shown"` is `promo_sheet_${id}_shown`), Kotlin/Java
|
|
136
|
+
`.name` / `name()` and Swift `.description` read an enum case's own name, and a
|
|
137
|
+
value only known at runtime is sent as unknown, never as an error (`--strict`
|
|
138
|
+
reports it). `--payloads` validates captured events instead, where a missing
|
|
139
|
+
required field IS an error; a value over the validator's size limits (name 500,
|
|
140
|
+
event type 100, field value 2000 characters, 200 fields) is sent as unknown and
|
|
141
|
+
flagged as an `oversize_value` warning on its line.
|
|
142
|
+
`check` reads nothing but the plan: any member's key works, `tk_r_` included.
|
|
143
|
+
|
|
144
|
+
`codegen` turns the plan into typed tracking code, from the same
|
|
145
|
+
`.tripl/check.yml`. It generates **per event-type style, never a function per
|
|
146
|
+
event**, and the generated code calls **your own wrapper** (the `transport`) —
|
|
147
|
+
or, with none configured, the shared `TriplDestination` in
|
|
148
|
+
`TriplTransport.swift` / `.kt` / `triplTransport.ts` that you implement once.
|
|
149
|
+
It never imports an SDK.
|
|
150
|
+
|
|
151
|
+
| `style` | What is generated |
|
|
152
|
+
|---------|-------------------|
|
|
153
|
+
| `structured` | an enum per plan field (its cases are the plan's values; a variable-backed value contributes its allowed values; free text stays `String`), your wrapper's call with its argument types narrowed — `log(category: Category, action: Action, label: String, properties:)` — and a compiler-checked `knownEvents` list |
|
|
154
|
+
| `screen_view` | the same, with `ScreenType` / `ScreenId` enums |
|
|
155
|
+
| `named` | one generic `track(event)`: Swift `enum LegacyEvent { case homeScreenView(HomeScreenView) … }` with `name` and `properties`; Kotlin a sealed interface of data classes/objects; TypeScript `track<K extends LegacyEventName>(name: K, props: LegacyEventProps[K])` |
|
|
156
|
+
| `self_describing` | one data class per schema with its fields, sharing one `track` |
|
|
157
|
+
|
|
158
|
+
```yaml
|
|
159
|
+
codegen:
|
|
160
|
+
out: {swift: Sources/Tracking/Generated, kotlin: app/src/main/java/tracking, ts: web/src/tracking}
|
|
161
|
+
kotlin_package: com.example.tracking
|
|
162
|
+
event_types:
|
|
163
|
+
se:
|
|
164
|
+
calls: [{function: "Analytics.shared.log", args: {category: category, action: action, label: label, properties: properties}}]
|
|
165
|
+
codegen:
|
|
166
|
+
style: structured # default: from the preset, else structured when the type has a name rule
|
|
167
|
+
transport:
|
|
168
|
+
swift: "Analytics.shared.log" # reuses the `calls` entry's args mapping
|
|
169
|
+
ts: {function: "analytics.log", positional: [category, action, label, properties],
|
|
170
|
+
import: "import { analytics } from './analytics';"}
|
|
171
|
+
type_names: {namespace: AppEvents, category: EventCategory, function: log}
|
|
172
|
+
template: {swift: .tripl/templates/structured.swift.mustache} # optional override
|
|
173
|
+
legacy:
|
|
174
|
+
calls: [{function: "Analytics.shared.logEvent", name_arg: 0, properties_arg: parameters}]
|
|
175
|
+
codegen: {style: named, languages: [swift, kotlin]}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
A `transport` given as a bare function reuses the `args` / `positional` /
|
|
179
|
+
`object_arg` mapping of the `calls` entry with the same `function`. Swift passes
|
|
180
|
+
labelled arguments with their labels, Kotlin as named arguments (give
|
|
181
|
+
`positional` for a Java wrapper), TypeScript positionally — or as one object
|
|
182
|
+
literal with `object_arg`. `type_names` renames `namespace`, `event` (the named
|
|
183
|
+
/ self-describing event type), `function`, and any field's enum by field name.
|
|
184
|
+
|
|
185
|
+
An event's parameters are the fields it does not fix plus one per `${token}` in
|
|
186
|
+
its name (`promo_sheet_${sheet_id}_shown` takes `sheetId`, typed by the
|
|
187
|
+
variable's allowed values). Plan strings become identifiers by splitting on
|
|
188
|
+
anything that is not a letter or digit and on camelCase: `Home Screen View` ->
|
|
189
|
+
`homeScreenView` (`HOME_SCREEN_VIEW` for Kotlin enum entries), `checkout:start`
|
|
190
|
+
-> `checkoutStart`; a leading digit gets `_` (`_1stRun`), a reserved word a
|
|
191
|
+
trailing `_` (`default_`), a reserved type name `Value` (`TypeValue`), and a
|
|
192
|
+
collision `2`, `3` in sorted order. The raw plan string is always what is sent.
|
|
193
|
+
Deprecated events are marked (`@available(*, deprecated)`, `@Deprecated`,
|
|
194
|
+
`@deprecated`); archived ones are not generated.
|
|
195
|
+
|
|
196
|
+
A custom `template` uses the built-in one's context (copy it from
|
|
197
|
+
`tripl_cli/codegen/templates/`): a Mustache subset — `{{name}}`, `{{a.b}}`,
|
|
198
|
+
`{{#list}}…{{/list}}`, `{{^empty}}…{{/empty}}`, `{{! comment }}` — where every
|
|
199
|
+
plan string arrives already quoted or escaped for the language, and an unknown
|
|
200
|
+
`{{name}}` is an error rather than an empty string.
|
|
201
|
+
|
|
202
|
+
Output is deterministic (sorted, no timestamps) with a `Generated by tripl
|
|
203
|
+
codegen — do not edit` header naming the project, branch and the plan's
|
|
204
|
+
content hash (not its revision, so a new revision that changes nothing is not
|
|
205
|
+
drift), so it can be committed: `tripl codegen --check` writes nothing and exits
|
|
206
|
+
1 when a file is missing, differs, or is stale (generated earlier, no longer
|
|
207
|
+
produced — a normal run deletes those, and only files carrying the header for
|
|
208
|
+
the same project, in a language the run writes into that directory). `--model FILE`
|
|
209
|
+
generates from a saved `tripl export --format codegen_model` without an
|
|
210
|
+
instance. `export` writes `bundle.json` and `<event type>/<identity>.schema.json`
|
|
211
|
+
per event (file names sanitised to `[A-Za-z0-9._-]`), or prints the export to
|
|
212
|
+
stdout without `--out`. Both read nothing but the plan: any member's key works.
|
|
213
|
+
|
|
97
214
|
`drifts reopen` is the one whose prompt is worth reading: reopening clears the
|
|
98
215
|
drift's `resolution_note`, `resolved_by` and `resolved_at`, and dismissing it
|
|
99
216
|
again does not bring them back.
|
|
@@ -210,10 +327,15 @@ not moved).
|
|
|
210
327
|
|
|
211
328
|
| Exit | Meaning |
|
|
212
329
|
|------|---------|
|
|
213
|
-
| 0 | Every check passed, or only warned and `--strict` was not given. `status`, whenever it completed. `watch`, whenever the run completed — a failed job or a new signal is still 0. The `scans` / `drifts` verbs
|
|
330
|
+
| 0 | Every check passed, or only warned and `--strict` was not given. `status`, whenever it completed. `watch`, whenever the run completed — a failed job or a new signal is still 0. The `scans` / `drifts` verbs and `annotate`, whenever every read arrived or the write was accepted (`--dry-run` included, and a de-duplicated `annotate` too). |
|
|
214
331
|
| 1 | The tool itself broke (doctor turns every API failure into a finding), or any other command could not complete a request — unreachable, or the API refused it. For `watch` this includes a key revoked mid-run. For `scans list` / `drifts list` it includes **any** failed read in the fan-out; for `scans run`, a job returned already `failed`; for `scans cancel` / `drifts dismiss` / `drifts reopen`, a declined prompt. |
|
|
215
|
-
| 2 | Usage or configuration error. For `doctor` and `status` that is resolved before any socket opens; `watch` also refuses after reading the listings, when `--scan` matches nothing or more than 24 scan configs are selected. The `scans` / `drifts` verbs add a bare group, a missing or repeated `--project`, an unresolved or ambiguous `<scan>`, and a prompting write on a non-TTY without `--yes
|
|
332
|
+
| 2 | Usage or configuration error. For `doctor` and `status` that is resolved before any socket opens; `watch` also refuses after reading the listings, when `--scan` matches nothing or more than 24 scan configs are selected. The `scans` / `drifts` verbs add a bare group, a missing or repeated `--project`, an unresolved or ambiguous `<scan>`, and a prompting write on a non-TTY without `--yes`; `annotate` adds a `--url` that is not http(s), an `--at` that is not RFC 3339, and half a scope. Either way **no JSON is emitted** and no write is sent. |
|
|
216
333
|
| 3 | `doctor` only: at least one check failed, or `--strict` and at least one warning. Nothing else ever exits 3. |
|
|
334
|
+
|
|
335
|
+
`check` uses 0, 1 and 2: 1 when any call site or event has an error (or, with
|
|
336
|
+
`--strict`, a warning), and 2 for a bad check config or payload file.
|
|
337
|
+
`codegen` uses them too: 1 for drift under `--check` (or a plan without a
|
|
338
|
+
configured event type), 2 for a bad check config or template.
|
|
217
339
|
| 130 | Interrupted (SIGINT). For `watch` this is the **normal** ending — a run without `--duration` has no other way to stop. |
|
|
218
340
|
|
|
219
341
|
An unreachable instance therefore exits **3** out of `doctor`, not 1 — it
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tripl"
|
|
3
|
-
# 0.2.
|
|
4
|
-
# 0.
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
version = "0.2.0"
|
|
3
|
+
# 0.2.2: `tripl_cli.api` gained the `docs` module and typed-property reads
|
|
4
|
+
# that tripl-mcp 0.2.2 imports, so this has to reach the index FIRST (tag
|
|
5
|
+
# `cli-v0.2.2`), then tripl-mcp 0.2.2. Versioned with the service release.
|
|
6
|
+
version = "0.2.2"
|
|
8
7
|
description = "Operator CLI for a tripl tracking-plan instance: diagnostics, health and live monitoring"
|
|
9
8
|
readme = "README.md"
|
|
10
9
|
requires-python = ">=3.12"
|
|
@@ -91,6 +90,8 @@ build-backend = "hatchling.build"
|
|
|
91
90
|
# package data already. Verified against a built wheel rather than assumed —
|
|
92
91
|
# `uv build --wheel` then unzip, and both files are there (tripl-ey6j.3). They
|
|
93
92
|
# are read through importlib.resources, which is why this matters at all.
|
|
93
|
+
# The same holds for src/tripl_cli/codegen/templates/*.mustache, the built-in
|
|
94
|
+
# `tripl codegen` templates (#262): package data by the same rule, read the same way.
|
|
94
95
|
packages = ["src/tripl_cli"]
|
|
95
96
|
|
|
96
97
|
[tool.pytest.ini_options]
|
|
@@ -46,10 +46,14 @@ from __future__ import annotations
|
|
|
46
46
|
from tripl_cli.api import (
|
|
47
47
|
auth,
|
|
48
48
|
branches,
|
|
49
|
+
chart_annotations,
|
|
49
50
|
data_sources,
|
|
51
|
+
docs,
|
|
50
52
|
event_types,
|
|
51
53
|
events,
|
|
52
54
|
monitoring,
|
|
55
|
+
plan_export,
|
|
56
|
+
plan_validation,
|
|
53
57
|
projects,
|
|
54
58
|
scans,
|
|
55
59
|
search,
|
|
@@ -65,12 +69,16 @@ __all__ = [
|
|
|
65
69
|
"ApiRequest",
|
|
66
70
|
"auth",
|
|
67
71
|
"branches",
|
|
72
|
+
"chart_annotations",
|
|
68
73
|
"data_sources",
|
|
74
|
+
"docs",
|
|
69
75
|
"event_types",
|
|
70
76
|
"events",
|
|
71
77
|
"monitoring",
|
|
72
78
|
"page_items",
|
|
73
79
|
"page_total",
|
|
80
|
+
"plan_export",
|
|
81
|
+
"plan_validation",
|
|
74
82
|
"projects",
|
|
75
83
|
"scans",
|
|
76
84
|
"search",
|
|
@@ -5,12 +5,13 @@ back to a human in the tripl UI (see ``tripl_mcp.tools.branches``). A builder
|
|
|
5
5
|
would be the first half of shipping them.
|
|
6
6
|
|
|
7
7
|
Note how the OTHER builders spell "the live plan": by leaving ``branch`` unset.
|
|
8
|
-
``deps.get_branch_id_override``
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
``deps.get_branch_id_override`` resolves main whenever ``?branch=`` is missing or
|
|
9
|
+
empty, and refuses anything that is not a UUID belonging to this project (400) or
|
|
10
|
+
names no branch of it (404). So there is no literal for main, here or anywhere.
|
|
11
|
+
It IS a declared query parameter, so ``backend/openapi.json`` carries it and the
|
|
12
|
+
contract test can see it — typed ``str`` rather than ``format: uuid`` because the
|
|
13
|
+
dependency parses the value itself, which is what keeps a malformed id a 400
|
|
14
|
+
instead of FastAPI's own 422.
|
|
14
15
|
"""
|
|
15
16
|
|
|
16
17
|
from __future__ import annotations
|
|
@@ -31,8 +32,9 @@ ENDPOINTS: tuple[tuple[str, str], ...] = (
|
|
|
31
32
|
def list_branches(slug: str, *, include_diff_counts: bool = False) -> ApiRequest:
|
|
32
33
|
"""Answers ``{items, total}``, not a bare list.
|
|
33
34
|
|
|
34
|
-
``include_diff_counts`` fills ``ahead`` and ``behind_base
|
|
35
|
-
|
|
35
|
+
``include_diff_counts`` fills ``ahead`` and ``behind_base`` on open branches
|
|
36
|
+
(merged and closed ones, like main, stay null); without it the service
|
|
37
|
+
leaves both null everywhere, because each row costs a snapshot comparison. Off
|
|
36
38
|
by default for that reason, and asked for in exactly one place: ``tripl plan
|
|
37
39
|
branches``, where those two columns are the reason to run the command at
|
|
38
40
|
all. The branch RESOLUTION path must keep the default — it needs only id and
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Chart annotations: the markers charts draw at a point in time.
|
|
2
|
+
|
|
3
|
+
Only the create route has a builder. ``tripl annotate`` is the one caller, and it
|
|
4
|
+
exists for CI: a deploy step posts "Deployed web 2026.09.25" with a link to the
|
|
5
|
+
release, and every monitoring (Volume tab) chart in the project shows it.
|
|
6
|
+
|
|
7
|
+
The route answers **201** for a new row and **200** when it de-duplicated: the
|
|
8
|
+
same label posted with source ``api`` to the same project within the last 24
|
|
9
|
+
hours returns the row that already exists instead of creating a second one.
|
|
10
|
+
Only ``api`` is de-duplicated here (``release`` is unique per label forever, and
|
|
11
|
+
is worker-only); ``manual`` creates are never de-duplicated and always 201. A retried CI job is
|
|
12
|
+
therefore safe to re-run, and the status code is the only way to tell the two
|
|
13
|
+
answers apart - the bodies have the same shape.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
|
|
20
|
+
from tripl_cli.api.request import ApiRequest
|
|
21
|
+
from tripl_cli.model import JsonDict, to_rfc3339
|
|
22
|
+
|
|
23
|
+
CREATE = "/projects/{slug}/annotations"
|
|
24
|
+
|
|
25
|
+
ENDPOINTS: tuple[tuple[str, str], ...] = (("post", CREATE),)
|
|
26
|
+
|
|
27
|
+
# `ChartAnnotationScopeType` verbatim. A scoped annotation only shows on charts
|
|
28
|
+
# of that scope; a project-level one (no scope) shows on every monitoring
|
|
29
|
+
# (Volume tab) chart in the project.
|
|
30
|
+
SCOPE_TYPES: tuple[str, ...] = ("project_total", "event_type", "event", "metric")
|
|
31
|
+
|
|
32
|
+
# What a client may send as `source`. `manual` is the app's own default and
|
|
33
|
+
# `release` is reserved to the metrics worker (the API answers 422), so the CLI
|
|
34
|
+
# always sends `api`.
|
|
35
|
+
SOURCE_API = "api"
|
|
36
|
+
|
|
37
|
+
# `ChartAnnotationCreate`'s own bounds, so a typo costs no request.
|
|
38
|
+
LABEL_MAX_LENGTH = 200
|
|
39
|
+
DESCRIPTION_MAX_LENGTH = 2000
|
|
40
|
+
SCOPE_REF_MAX_LENGTH = 120
|
|
41
|
+
URL_MAX_LENGTH = 500
|
|
42
|
+
|
|
43
|
+
# 201 is a new annotation; 200 is the one that already existed.
|
|
44
|
+
STATUS_CREATED = 201
|
|
45
|
+
STATUS_DEDUPLICATED = 200
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def create_annotation(
|
|
49
|
+
slug: str,
|
|
50
|
+
*,
|
|
51
|
+
label: str,
|
|
52
|
+
at: datetime,
|
|
53
|
+
url: str | None = None,
|
|
54
|
+
description: str | None = None,
|
|
55
|
+
scope_type: str | None = None,
|
|
56
|
+
scope_ref: str | None = None,
|
|
57
|
+
) -> ApiRequest:
|
|
58
|
+
"""``POST /projects/{slug}/annotations`` with ``source="api"``.
|
|
59
|
+
|
|
60
|
+
Editor-gated: a ``tk_w_`` key backed by an editor or owner. ``bucket`` is the
|
|
61
|
+
route's name for the timestamp. Unset members are omitted rather than sent
|
|
62
|
+
as null, so the server's defaults (the colour among them) apply.
|
|
63
|
+
"""
|
|
64
|
+
body: JsonDict = {"label": label, "bucket": to_rfc3339(at), "source": SOURCE_API}
|
|
65
|
+
if url is not None:
|
|
66
|
+
body["url"] = url
|
|
67
|
+
if description is not None:
|
|
68
|
+
body["description"] = description
|
|
69
|
+
if scope_type is not None:
|
|
70
|
+
body["scope_type"] = scope_type
|
|
71
|
+
if scope_ref is not None:
|
|
72
|
+
body["scope_ref"] = scope_ref
|
|
73
|
+
return ApiRequest("POST", CREATE.format(slug=slug), json_body=body)
|