feat(dify): identify plugin requests with X-TF-Client-Name/Version - #36
Conversation
Search and Fetch calls from this plugin land in TinyFish telemetry as untagged request_origin=api. Runs were already tagged via the api_integration payload field, but search/fetch have no payload to carry it. The server reads X-TF-Client-Name / X-TF-Client-Version on every route. - _api_headers adds X-TF-Client-Name: tinyfish-dify and X-TF-Client-Version read from manifest.yaml; both call sites (_tf_request and run_sse's stream) already use it - tools/constants.PLUGIN_VERSION parsed from the manifest so the header cannot drift from the published version - bump 0.0.4 → 0.0.5 (both manifest version fields, matching prior bumps) - tests/test_headers.py: header contract and version/manifest agreement X-TF-Request-Origin deliberately unset: `api` stays the transport. Attributes new traffic only; needs a marketplace republish. Testing: pytest 2 passed. Repo has no dify lint/test CI; ruff findings in untouched tool files are pre-existing. Co-Authored-By: Claude Code <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138Vn8E8TfZkUWd3StqDgjp
Review fixes. The packager honors .difyignore only, so tests/ was shipping in the marketplace artifact. The manifest test re-parsed the file the same way the constant does and opened it relative to cwd, so it could only fail by being run from the wrong directory. - .difyignore: tests/ - test asserts a real semver and that the manifest carries it, path resolved from __file__ - comment: request_origin stays `api`; what changes is client_name Testing: pytest 2 passed from dify/ and from the repo root. Co-Authored-By: Claude Code <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138Vn8E8TfZkUWd3StqDgjp
The substring check also matched the indented meta.version line. Co-Authored-By: Claude Code <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138Vn8E8TfZkUWd3StqDgjp
Aligns X-TF-Client-Name with the repo contract (bare token, same as api_integration) and replaces the hand-rolled version scanner with yaml.safe_load — pyyaml is already a hard dependency of dify_plugin and is the loader the Dify daemon uses on the same file. - X-TF-Client-Name: dify - constants: yaml.safe_load(manifest)["version"], 0+unknown on any failure - requirements: pyyaml explicit instead of transitive - test: column-0 anchor on the manifest check Testing: pytest 2 passed (uv run --with pytest --with httpx --with pyyaml). Co-Authored-By: Claude Code <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138Vn8E8TfZkUWd3StqDgjp
📝 WalkthroughWalkthroughThe Dify plugin now loads its version from Priority: ⬇️ Low — Defer this Dify plugin metadata change because it adds client-identification headers and version packaging without supplied evidence of broader product impact. Merge Risk: 🟡 Moderate · up to The plugin manifest may be rejected or misinterpreted by Dify. Restore the manifest-format version before merging. 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
#35 dropped `dify` from the `X-TF-Client-Name` row because it did not send the headers; this PR is the one that makes it comply. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nw7GKwVBvSXErUW8wkxaYD
dify search/fetch/GET calls land with
client_namenull, indistinguishable from raw curl inrequest_origin=api.api_integrationrides run payloads only; GET and search/fetch have no body. server readsX-TF-Client-Name/X-TF-Client-Versionon every route (request-source.ts:160)._api_headerssendsX-TF-Client-Name: dify+X-TF-Client-Versionfrommanifest.yaml. both outbound sites (_tf_request,run_ssestream) already use itPLUGIN_VERSION=yaml.safe_load(manifest)["version"]. pyyaml already transitive viadify_plugin(pyyaml>=6.0.3), now explicitversionfields.difyignore:tests/, keeps them out of.difypkgdifyback in header-contract tableX-TF-Request-Originunset.apiis transport, server derives itnotes
dify/.github/workflows/plugin-publish.yml, not repo root, so GitHub ignores it. marketplace republish manual. pre-existing, own fixuv run --with pytest --with httpx --with pyyaml pytest dify/testsclient_nameself-reported. analytics only, never auth or billingtools/run_*.py,get_run.py,list_runs.pypre-existing, untouched