ClaudeMods
☰
ZH-TW
● 0 人在線上 · 瀏覽 0 次
贊助提交作品
官方範例 · 發布者 anthropics

telemetry

外掛分析:hook $.telemetry 的兩個事件,讓外掛記錄事件,或把功能使用標記成一筆 first-party 記錄,並用工作階段自己的認證分批送出。

已翻譯

關於這個 mod

telemetry

這是一個以外掛形式提供的外掛分析 mod。它對 telemetry.log 和 telemetry.mark 的 hook 實作這兩個事件,建立在 engine.create 步驟交給它的那些名詞上;上方的閘門只服務 Claude Code 內建的外掛:由使用者安裝或管理員列出的外掛發起的呼叫,會帶著原因被拒絕(主機會在每次呼叫上標記發起它的外掛、next.origin,閘門則讀取它的層級)。如果某個引擎沒有自己的 $.telemetry,同一個步驟會補上這個名詞,因此這些呼叫在那裡也存在。項目用 to 表示去向:anthropic(預設)是這個 mod 的去向;collector 則是操作員設定的 telemetry collector,會原樣傳給下層,而 to 永遠不會成為記錄的一部分。 $.telemetry.log({ event, props }) 會把一個事件排入佇列,成為一筆 first-party 記錄 tengu_plugin_<event>;$.telemetry.mark({ feature, kind, reason?, props? }) 會像 CLI 自己的功能事件一樣,把一次功能使用標記成 tengu_feature_<kind>,旁邊放上 feature_name 和這次標記的屬性。兩者都會在記錄排入佇列後完成。記錄會分批送出:第一筆記錄排隊幾秒後,用工作階段自己的認證向事件記錄接收端發出一次 POST(每個批次都會解析 $.session.authorize());等待中的數量達到 100 時立即送出,工作階段結束時也會送出。如果接收端因伺服器錯誤、逾時或速率限制拒絕批次,會再試一次。沒有 first-party 認證的工作階段,或仍被接收端拒絕的批次,會被丟棄;每種結果都會在 debug log 中佔一行。

每筆記錄都帶有 CLI 自己的記錄會帶的內容,這些內容透過 $ 在每個工作階段收集一次:事件 ID、安裝的裝置 ID、CLI 全域設定中已登入帳戶的 ID、工作階段 ID、模型、使用者端類型、進入點和互動性,以及 env 區塊(一次 uname 探測得到的平台和架構、終端機、shell、套件管理器和執行階段、CI 與 GitHub Actions、遠端容器、部署、Linux 發行版和核心、WSL、工作目錄的版本控制,以及透過 $.session.version() 取得的引擎版本、基礎版本和建置時間;引擎沒有回應時留白),並在記錄屬性旁放上儲存庫的遠端 hash。只有引擎知道的內容(執行階段版本、程序記憶體、請求的 betas、訂閱層級、呼叫代理)不在 $ 上,因此這些欄位保持空白。

只要 CLI 自己的分析功能關閉,它就不會送出任何內容:處於 DISABLE_TELEMETRY、CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC 或 DO_NOT_TRACK 之下時、測試執行時、使用主機不管理的任何第三方供應商(Bedrock、Vertex、Foundry 等)時、位於雲端 gateway(環境開關或受管理政策的登入 pins)時,以及位於擁有自己 OAuth URL 的部署中時,都會停止送出。每個批次送出前都會透過 $.env 和 $.settings 重新讀取這些設定,因此工作階段後來切換到第三方供應商或 gateway 後就不會再送出;如果無法讀取開關,也不會送出。USER_TYPE 表示對應情況時,記錄的 user_type 是 ant,否則是 external。

記錄中不會出現自由格式內容。事件名稱和每個屬性鍵都是 snake_case token;值只能是有限數字、布林值或 Choice(和可選清單一起命名的字串),log 和 mark 都一樣。mark 接受 ok、sad 或 bad;後兩者必須提供 reason,前者則不接受 reason。違反規則的項目會在排入佇列前被拒絕。對於環境變數,如果變數的值是秘密,或包含人員或主機名稱,只會讀取它是否已設定,不會讀取其他內容;shell 則是來自封閉清單的 basename。

hooks/register.ts 是模組;types/index.d.ts 是這個名詞的契約,也是這個 mod 的 hook、呼叫該名詞的 mod,以及回應它的測試共同讀取的 $.telemetry 唯一宣告。

它 hook 的內容

telemetry.* 是閘門:內建層級(或引擎)的呼叫會繼續,其他呼叫會被拒絕,閘門本身擲出例外時也會拒絕。 telemetry.log 和 telemetry.mark 位於閘門下:檢查項目並將記錄排入佇列,hook 會回應 { value };如果項目違反規則,則回應帶有原因的 { deny },讓呼叫方的 promise 以該名稱拒絕。engine.create:sender 建立在 await next(e) 之上,這個步驟向上交出 { ...{ telemetry }, ...beneath };beneath 會最後展開,因此下層已有自己的 telemetry 時保留它,沒有時才加入這個 mod 的 telemetry;下層沒有任何內容不會被取代。 session.start 用來得知使用者是否在提示列前;session.end 用來送出仍在等待的內容。

它在 $ 上呼叫的內容

session.authorize、session.id、session.model、session.surfaces、session.cwd、session.repo、settings.read、env.get(開關和描述用的變數,按字面名稱讀取)、fs.read、fs.list、fs.exists、process.run(一次執行 uname 和 command -v 的 sh -c)、clock.after、clock.sleep、http.fetch 和 ui.log(寫入 debug log),都透過 fold 交給它的介面呼叫。

執行位置與服務對象

這個外掛由 CLI 本身載入,在自身分析功能開啟的每個建置中執行,其他地方都不執行;它服務 CLI 內建的外掛,拒絕其他所有呼叫方。它不是設計來透過 --plugin-dir 安裝或載入的;資料夾帶有資訊清單,是為了讓它像其他外掛一樣被讀取,而不是為了獨立執行。內建外掛在這個外掛不存在時呼叫 $.telemetry,會找不到這個名詞;如果引擎有自己的名詞,則會找到一個不會排入任何內容的版本。不論哪種情況,都是 "no analytics here"。

安裝

請先查看作者 README,確認 marketplace 與外掛名稱;指令可能隨儲存庫結構而變動。

claude plugin marketplace add anthropics/claude-code
claude plugin install telemetry
原文 / README

telemetry

Plugin analytics as a plugin: its hooks on telemetry.log and telemetry.mark are what those two events do, built over the nouns its engine.create step is handed, and a gate above them serves the plugins built into Claude Code alone: a call from a plugin a person installed or an administrator listed is refused with a reason (the host stamps every call with the plugin that raised it, next.origin, and the gate reads its tier). On an engine that has no $.telemetry of its own the same step adds the noun, so the calls exist there too. An entry names where it goes with to: anthropic, the default, is this mod's; one for collector, the telemetry collector an operator configured, is passed on beneath untouched, and to is never part of a row. $.telemetry.log({ event, props }) queues one event as one first-party row, tengu_plugin_<event>; $.telemetry.mark({ feature, kind, reason?, props? }) marks one use of a feature as the CLI's own feature events do, tengu_feature_<kind> with a feature_name and the mark's properties beside it. Both resolve once the row is queued. Rows go out in batches: one POST to the event-logging ingest with the session's own credential ($.session.authorize(), resolved for each batch) a few seconds after the first row was queued, at once when a hundred wait, and when the session ends; a batch the ingest refuses with a server error, a timeout or a rate limit is tried once more. A session with no first-party credential, or an ingest that still refuses, drops the batch; each outcome is one line in the debug log.

Each row carries what the CLI's own rows carry, gathered through $ once a session: an event id, the install's device id and the signed-in account's ids from the CLI's global config, the session's id, model, client type, entrypoint and interactivity, and an env block (platform and architecture from one uname probe, terminal, shell, package managers and runtimes, CI and GitHub Actions, the remote container, the deployment, the Linux distribution and kernel, WSL, the working directory's version control, and the engine's version, base version and build time from $.session.version(), left empty on an engine that does not answer it), with the repository's remote hash beside the row's properties. What the engine alone knows (its runtime's version, the process's memory, the request's betas, the subscription tier, the calling agent) is not on $, and those columns stay empty.

It sends nothing wherever the CLI's own analytics are off: under DISABLE_TELEMETRY, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC or DO_NOT_TRACK, in a test run, on any third-party provider (Bedrock, Vertex, Foundry and kin) the host does not manage, on a cloud gateway (the environment's switch or the managed policy's login pins), and on a deployment with its own OAuth URL. Each is read through $.env and $.settings before every batch, so a session that has since moved to a third-party provider or a gateway sends nothing more; when the switches cannot be read, nothing is sent either. The row's user_type is ant when USER_TYPE says so, else external.

Nothing free-form reaches a row. An event name and every property key is a snake_case token; a value is a finite number, a boolean, or a Choice (a string named together with the list it is chosen from), under log and mark alike; mark takes ok, sad or bad, with a reason required on the last two and refused on the first. An entry that breaks a rule is refused before anything is queued. Of the environment, a variable whose value is a secret, or names a person or a host, is read for whether it is set and nothing more; a shell is its basename from a closed list.

hooks/register.ts is the module; types/index.d.ts is the noun's contract, the one declaration of $.telemetry that this mod's hooks, a mod calling the noun and a test answering it all read.

What it hooks

telemetry.*, the gate: a caller in the built-in tier (or the engine) goes on, any other is refused, and a gate that throws refuses too. telemetry.log and telemetry.mark, beneath the gate: the entry is checked and its row queued, the hook answering { value }, or { deny } with the reason for an entry that breaks a rule, so the caller's promise rejects naming it. engine.create: the sender is built over await next(e), and the step hands up { ...{ telemetry }, ...beneath }: what is beneath is spread last, so its own telemetry stands where it has one and this mod's is added where it has none; nothing beneath is replaced. session.start, to learn whether a person is at the prompt; session.end, to send what still waits.

What it calls on $

session.authorize, session.id, session.model, session.surfaces, session.cwd, session.repo, settings.read, env.get (the switches and the describing variables, by literal name), fs.read, fs.list, fs.exists, process.run (one sh -c of uname and command -v), clock.after, clock.sleep, http.fetch and ui.log (to the debug log), each on the interface the fold handed it.

Where it runs, whom it serves

This plugin is seated by the CLI itself, on every build whose own analytics are on, and nowhere else; it serves the plugins bundled with the CLI and refuses every other caller. It is not meant to be installed or loaded with --plugin-dir; the folder has a manifest so it reads like every other plugin, not so it can stand alone. A built-in that calls $.telemetry where this one is absent finds no such noun, or, on an engine with the noun of its own, one whose calls queue nothing; either way that is "no analytics here".

更多類似作品