ClaudeMods
☰
KO
● 0 명 접속 중 · 조회 0 회
후원프로젝트 제출
공식 예제 · 작성자 anthropics

telemetry

플러그인 분석: $.telemetry의 두 이벤트를 hook해 플러그인이 이벤트를 기록하거나 기능 사용을 first-party 행으로 표시하고 세션 자체의 자격 증명으로 배치 전송합니다.

번역 완료

이 mod 소개

telemetry

플러그인으로 제공되는 플러그인 분석입니다. 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과 mark의 속성을 옆에 둡니다. 둘 다 행이 큐에 들어가면 완료됩니다. 행은 배치로 나갑니다. 첫 행이 큐에 들어간 뒤 몇 초 후 세션 자체의 자격 증명으로 이벤트 로깅 수집기에 POST를 한 번 보내며(각 배치에서 $.session.authorize()를 확인), 100개가 기다리면 즉시 보내고 세션이 끝날 때도 보냅니다. 수집기가 서버 오류, 시간 초과 또는 속도 제한으로 배치를 거부하면 한 번 더 시도합니다. first-party 자격 증명이 없는 세션이나 수집기가 계속 거부하는 배치는 버리며 각 결과를 debug log에 한 줄로 남깁니다.

각 행은 CLI 자체 행이 담는 내용을 포함하고, 이를 세션마다 한 번 $을 통해 모읍니다. 이벤트 ID, 설치의 장치 ID, CLI 전역 설정에 있는 로그인 계정 ID, 세션 ID, 모델, 클라이언트 유형, 진입점과 상호작용성, 그리고 env 블록(platform과 architecture는 한 번의 uname probe에서, terminal, shell, package manager와 runtime, CI와 GitHub Actions, remote container, deployment, Linux distribution과 kernel, WSL, 작업 디렉터리의 버전 관리, 엔진이 답할 때 $.session.version()에서 얻는 engine version·base version·build time이며 답하지 않으면 비워 둠)을 담고 행 속성 옆에 저장소의 remote hash를 둡니다. 엔진만 아는 것(runtime version, 프로세스 메모리, 요청의 betas, subscription tier, 호출 agent)은 $에 없으므로 해당 열은 비워 둡니다.

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이고 값은 유한한 수, boolean, 또는 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는 그 이름을 포함해 reject됩니다. engine.create에서는 sender가 await next(e) 위에 구축되고 단계는 { ...{ telemetry }, ...beneath }를 위로 전달합니다. beneath가 마지막에 펼쳐지므로 아래 계층에 자체 telemetry가 있으면 그것이 유지되고 없을 때만 이 mod의 것이 추가됩니다. 아래 계층의 것은 대체하지 않습니다. 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로 설치하거나 로드하려는 용도가 아닙니다. 폴더에 manifest가 있는 것은 다른 플러그인처럼 읽히게 하려는 것이지 단독으로 실행하기 위해서가 아닙니다. 이 플러그인이 없는 곳에서 내장 플러그인이 $.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".

비슷한 프로젝트