feat: identify every gitops API request with a User-Agent - #78
Open
scott-lowe-vapi wants to merge 1 commit into
Open
scott-lowe-vapi wants to merge 1 commit into
scott-lowe-vapi wants to merge 1 commit into
Conversation
Only `npm run sim` and `npm run check` identified themselves. Setup, pull, push, apply, promote, cleanup, rollback, call and audit sent Node's default `node` User-Agent, about a sixth of all api.vapi.ai traffic, so gitops usage beyond simulations couldn't be counted. - src/user-agent.ts: `vapi-gitops-<command>/<version>`, plus ` (ci)` when CI or GITHUB_ACTIONS is set. The command is the npm script that started the process (so `npm run apply` labels the pull and push it runs as apply, and a promotion's applies as promote), else the entry script's name. sim and check keep their fixed labels, which analytics already counts simulation runs by. - Every fetch to the Vapi API sends it. tests/user-agent-coverage.test.ts fails on a fetch without it and checks api.ts against a local server. - cleanup-safety and new-file-gate tests sent about a dozen requests to the real api.vapi.ai per `npm test` (fake key, 401s), which the new User-Agent made visible in the request logs, and which would have counted every fork's CI run as usage. They now point at a dead local address. - how-it-works.md says what the API sees; AGENTS.md says every request sends the header. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This was referenced Oct 3, 2026
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Value
V.A.L.U.E. tier: small — a behavior change: every API request now carries a new header. No blast-radius path.
npm run simandnpm run checkidentify themselves. Setup, pull, push, apply, promote, cleanup, rollback, call and audit send Node's default User-Agent,node, which was 126k of 728kapi.vapi.airequests in one hour this morning. So "how many deploys come from gitops, from CI or from laptops" has no answer.src/user-agent.tsbuildsvapi-gitops-<command>/<version>, plus(ci)whenCIorGITHUB_ACTIONSis set:<command>is the npm script that started the process. Sonpm run applylabels the pull and push it runs asapply, and a promotion's applies are labelledpromote.simandcheckkeep their fixed labels, which existing simulation analytics already counts by.fetchto the Vapi API sends it:api.ts(push, pull, apply, promote), cleanup, setup, the interactive pickers, rollback, call, and push's direct fetch.cleanup-safetyandnew-file-gatesent about 12 requests pernpm testtoapi.vapi.ai, with a fake key, getting 401s. That breaks the repo's own rule that tests never call the real API, and with this PR it would have counted every fork's CI run as gitops usage. They now point at a dead local address.how-it-works.mdsays exactly what the API sees (and that there is no other telemetry).AGENTS.mdsays every request must send the header.Evidence of value
Live, through the Cloudflare request logs in Axiom (
cloudflare-logpush): a read-onlynpm run setup -- ua-check --resources noneagainst the test org, run from a scratch copy:vapi-gitops-setup/1.0.0vapi-gitops-test/1.0.0npm testruns before the fixBefore this PR, both rows would have been indistinguishable
nodetraffic.Tests:
tests/user-agent-coverage.test.tsreads everyfetch(insrc/and requires aUser-Agent. On the parent branch it lists 10 call sites without one; here it lists none. It also runsapi.tsagainst a local server withnpm_lifecycle_event=apply.tests/user-agent.test.tspins the format: fixed sim and check labels, npm script vs. entry script vs.cli, label cleaning, and the CI marker forGITHUB_ACTIONS=true,CI=trueandCI=1(but notfalse,0or empty).fetchtrap that records any request tovapi.aicaught 12 requests before the test fix and none after.Testing plan
npm test(527 tests) andnpx tsc --noEmitpass.1.0.0; bumping it is deliberately left out of this PR.callcommand's WebSocket audio connection isn't a Vapi REST request and doesn't carry the header.Refs TEST-141
🤖 Generated with Claude Code