Repository navigation
Refactor telemetry to use detached worker process - #65
Merged
Merged
Conversation
trackInstall() POSTs the skill_key payload, but the call was fire-and-forget (`void`) and process.exit() ran immediately after, aborting the in-flight request. Only the trackCommand event (fired at command start, with no skill_key) reliably reached the backend, making it look like skill_key was missing from add/install events. Collect the trackInstall promises and await Promise.allSettled before exiting. sendTelemetry already enforces a 5s timeout, so the flush cannot hang.
The previous fix awaited trackInstall before process.exit so the POST carrying skill_key would actually flush. That worked, but it put the telemetry endpoint on the user's critical path — a slow or down server could add up to 5s of wall-clock latency to every successful install. Move telemetry to the standard CLI pattern used by Next.js, Gatsby, etc: spawn a detached child process that owns the HTTP request and survives the parent's exit. - New src/telemetry-worker.ts: reads JSON payload from stdin, POSTs it with a 5s timeout, exits silently on success or failure. - telemetry.ts spawns the worker with detached:true and unref(), then writes the payload to its stdin and returns immediately. Small JSON fits in the kernel pipe buffer so the write completes synchronously and the worker reads it after the parent exits. - trackCommand/trackInstall now return void (truly fire-and-forget); the `void` prefix at call sites was misleading and is removed. - Telemetry is auto-skipped when loaded from .ts source (tsx in dev/test) since the compiled worker only ships in dist/. Smoke-tested against a local fake endpoint: parent spawn+stdin-write took ~4ms and the server received the full payload including skill_key.
- trackInstall now accepts the list of agent names the skill was installed to (e.g. ['Claude Code', 'Cursor']). De-duplicated before send. Lets us answer "which agents are people actually installing to?" without collecting any PII — agent names are product names, not user identifiers. - Loosen the DO_NOT_TRACK / CI env check: any non-empty value other than "0" / "false" disables. Previously only literal "1" worked, so DO_NOT_TRACK=true silently did nothing. - Expand README disclosure: list every field we send, document the detached-worker dispatch, link the DNT convention. Smoke-tested against a local fake endpoint: agents: install event arrived with agents:["Claude Code","Cursor"] DNT=true: no request fired DNT=0: request fired (per DNT spec)
The telemetry_events table column is named 'platform', not 'agents'. Renaming the install payload field so the backend reads the value without a translation layer. Smoke-tested: wire payload now arrives as "platform":["Claude Code","Cursor"]
3 tasks done
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.
Summary
Refactors the telemetry system to dispatch events to a detached child process (
telemetry-worker.js) instead of making HTTP requests directly from the CLI. This ensures the CLI returns to the user immediately without being blocked by network I/O, even ifprocess.exit()is called in command code.Key improvements:
process.exit()in commands no longer aborts in-flight telemetrytrackInstall()to capture platform/agent names where skills were installedChanges
Core Telemetry Refactor
src/telemetry.ts: Refactored to spawn a detached worker process instead of making direct HTTP requeststelemetry-worker.tstrackCommand()andtrackInstall()to returnvoidinstead ofPromise<void>isTelemetryDisabled()helper with improved env var handling (supports any truthy value, not just "1"/"true")child.unref()src/telemetry-worker.ts(new): Standalone worker that reads JSON from stdin and POSTs to telemetry endpointEnhanced Install Tracking
src/telemetry.ts:trackInstall()now accepts optionalplatformparameter (agent names)src/commands/install.ts: Passesresult.installed.map((i) => i.agent)totrackInstall()src/commands/add.ts: Passesresult.installed.map((i) => i.agent)totrackInstall()Call Site Updates
voidprefix from alltrackCommand()andtrackInstall()calls (now returnvoid, notPromise)add.ts,bundle.ts,init.ts,install.ts,list.ts,remove.ts,search.ts,submit.ts,update.tsDocumentation
README.md: Expanded telemetry section with:DO_NOT_TRACKandCIenv varsType of Change
Testing
The refactoring maintains backward compatibility in telemetry behavior:
DO_NOT_TRACK=1andCI=truestill worksBuild verification:
npm run buildwill compile bothtelemetry.tsand newtelemetry-worker.tstodist/.https://claude.ai/code/session_01NNWdGxePTFy2h67N4bFjNy