feat(omo-codex): wire Codex SessionStart telemetry for DAU/WAU/MAU
Adds a new Codex plugin component `telemetry` that emits a single omo_codex_daily_active event (reason: session_start) from every Codex SessionStart hook, with the same UTC-day deduplication, hashed installation identifier, and four-flag opt-out as the install-time event. Previously omo-codex telemetry only fired on install_completed, so DAU/WAU/MAU under-reported real Codex usage. - New plugin component packages/omo-codex/plugin/components/telemetry/ mirrors the rules/comment-checker/lsp pattern: own src/, tsc build, vitest tests, package.json (posthog-node dep), hooks/hooks.json. - src/codex-hook.ts wraps createPluginPostHog().trackActive(..., "session_start") with safeShutdown so Codex session startup never blocks on telemetry. - Plugin root hooks.json + workspaces register the new component alongside rules and ultrawork on SessionStart. - Aggregate test expectations updated to include the telemetry directory. - cross-package-equivalence.test.ts pins product-identity constants and shouldDisablePostHog behavior to stay byte-equivalent between the CLI installer (src/telemetry/) and the plugin runtime (plugin/components/telemetry/src/), so the two PostHog sources never drift on event name, distinct_id base, dedup file path, or opt-out flags. - PostHogActivityReason union in the CLI-side posthog.ts gains "session_start" so future CLI paths can emit the same reason without a type break.
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# codex-telemetry
|
||||
|
||||
Codex plugin component that emits a single anonymous daily-active event (`omo_codex_daily_active`) to PostHog whenever a Codex session starts.
|
||||
|
||||
The event is sent **at most once per UTC day per machine**. It uses a SHA256-hashed installation identifier derived from `omo-codex:${hostname}` and never sends the raw hostname. PostHog person profiles are explicitly disabled.
|
||||
|
||||
## Hook Wiring
|
||||
|
||||
The component registers a single `SessionStart` hook:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook session-start",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The aggregate `plugin/hooks/hooks.json` mounts this hook alongside `rules` and `ultrawork` so all three fire in parallel at the start of every Codex session.
|
||||
|
||||
## What Is Captured
|
||||
|
||||
A single PostHog `capture` call with:
|
||||
|
||||
- `event: "omo_codex_daily_active"`
|
||||
- `distinctId: sha256("omo-codex:" + hostname)`
|
||||
- `properties`:
|
||||
- `platform`, `product_name`, `package_name`, `package_version`
|
||||
- `runtime` (`"node"`), `runtime_version`
|
||||
- `source: "plugin"`, `reason: "session_start"`
|
||||
- `$os`, `$os_version`, `os_arch`, `os_type`
|
||||
- `cpu_count`, `cpu_model`, `total_memory_gb`
|
||||
- `locale`, `timezone`, `shell`, `ci`, `terminal`
|
||||
- `day_utc` (today's UTC date)
|
||||
- `$process_person_profile: false`
|
||||
|
||||
The component never sends prompt contents, file contents, API keys, raw hostnames, or any user-identifying data.
|
||||
|
||||
## Opt-Out
|
||||
|
||||
Set any of the following environment variables before launching Codex:
|
||||
|
||||
```bash
|
||||
# Codex-only opt-out
|
||||
export OMO_CODEX_DISABLE_POSTHOG=1
|
||||
export OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0
|
||||
|
||||
# Global opt-out (covers both omo and omo-codex)
|
||||
export OMO_DISABLE_POSTHOG=1
|
||||
export OMO_SEND_ANONYMOUS_TELEMETRY=0
|
||||
```
|
||||
|
||||
When any of these is set the component creates a no-op PostHog client and exits without any network call.
|
||||
|
||||
## Daily Deduplication
|
||||
|
||||
The component writes a small JSON state file at:
|
||||
|
||||
```
|
||||
$XDG_DATA_HOME/omo-codex/posthog-activity.json
|
||||
# or, when XDG_DATA_HOME is unset:
|
||||
~/.local/share/omo-codex/posthog-activity.json
|
||||
```
|
||||
|
||||
containing `{ "lastActiveDayUTC": "YYYY-MM-DD" }`. If the stored day matches today (UTC), the hook returns without sending anything. The file is written atomically via `rename(2)`.
|
||||
|
||||
## Failure Behavior
|
||||
|
||||
Every telemetry path is wrapped in `try`/`catch`. The hook always exits 0 with no stdout output, even when PostHog construction, capture, or shutdown fails. Codex session startup is never blocked or slowed by telemetry failures.
|
||||
|
||||
## Endpoint Overrides
|
||||
|
||||
| Variable | Default |
|
||||
|----------|---------|
|
||||
| `POSTHOG_HOST` | `https://us.i.posthog.com` |
|
||||
| `POSTHOG_API_KEY` | shared `omo-codex` project key |
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm test # vitest (in-process + subprocess CLI smoke)
|
||||
npm run typecheck
|
||||
npm run build # tsc -> dist/
|
||||
npm run check # typecheck + biome + build
|
||||
```
|
||||
|
||||
The component shares its product identity constants with the `@oh-my-opencode/omo-codex` CLI installer. Drift between the two implementations is guarded by `packages/omo-codex/src/telemetry/cross-package-equivalence.test.ts`.
|
||||
|
||||
## Privacy
|
||||
|
||||
See [the omo Privacy Policy](https://github.com/code-yeongyu/oh-my-openagent/blob/dev/docs/legal/privacy-policy.md) for the full disclosure.
|
||||
Reference in New Issue
Block a user