docs(omo-codex): batch 74 (7 files)
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
# Repository Conventions
|
||||||
|
|
||||||
|
Conventions for human contributors and AI agents working on this component.
|
||||||
|
|
||||||
|
## Style
|
||||||
|
|
||||||
|
- Terse technical prose. No emojis in commits, issues, PR comments, or code.
|
||||||
|
- TypeScript strict mode. No `any`, no `@ts-ignore`, no `@ts-expect-error`, no enums.
|
||||||
|
- ESM modules with `.js` suffix in runtime import paths.
|
||||||
|
- Tabs for indentation. Double quotes for strings.
|
||||||
|
- Tests use vitest with `#given .. #when .. #then` descriptions or plain `// given / // when / // then` body comments.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
- `npm install` - install dependencies.
|
||||||
|
- `npm test` - run vitest once.
|
||||||
|
- `npm run typecheck` - strict TypeScript check.
|
||||||
|
- `npm run check` - type check, biome, and build.
|
||||||
|
- `npm run build` - emit `dist/`.
|
||||||
|
- `node dist/cli.js hook session-start < fixture.json` - smoke-test the SessionStart hook.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- No Bun APIs. Runtime is Node only because Codex launches plugin hooks with Node.
|
||||||
|
- The single hook handler is `runSessionStartHook`. Do not add new hook handlers without also wiring them in `hooks/hooks.json` and `plugin/hooks/hooks.json`.
|
||||||
|
- Telemetry MUST be silent on every failure path. The CLI MUST exit 0 with empty stdout even when PostHog construction, capture, or shutdown throws.
|
||||||
|
- Telemetry MUST be daily-deduplicated. Adding a new event type requires a new state file slot, not removal of the existing dedup.
|
||||||
|
- Hook output MUST stay empty (no `additionalContext`, no `systemMessage`). This component is observability-only and MUST NOT inject context into the Codex conversation.
|
||||||
|
- Constants in `src/product-identity.ts` MUST stay byte-equivalent with `packages/omo-codex/src/telemetry/product-identity.ts`. The cross-package equivalence test will fail otherwise.
|
||||||
|
- Do not couple this component back to omo internal source paths beyond what `cross-package-equivalence.test.ts` already asserts at the constants layer.
|
||||||
|
|
||||||
|
## Don'ts
|
||||||
|
|
||||||
|
- No `git add -A` or `git add .`. Stage only the files you changed.
|
||||||
|
- No `git commit --no-verify`. No force pushes. No history rewriting on shared branches.
|
||||||
|
- No new network calls. PostHog is the only allowed sink.
|
||||||
|
- No new env vars without README + privacy-policy update.
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true,
|
||||||
|
"style": {
|
||||||
|
"noDefaultExport": "error",
|
||||||
|
"noEnum": "error",
|
||||||
|
"noNonNullAssertion": "error",
|
||||||
|
"useImportType": "error",
|
||||||
|
"useConst": "error",
|
||||||
|
"useNodejsImportProtocol": "off"
|
||||||
|
},
|
||||||
|
"complexity": {
|
||||||
|
"useLiteralKeys": "off"
|
||||||
|
},
|
||||||
|
"suspicious": {
|
||||||
|
"noExplicitAny": "error",
|
||||||
|
"noTsIgnore": "error",
|
||||||
|
"noControlCharactersInRegex": "off",
|
||||||
|
"noEmptyInterface": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"formatWithErrors": false,
|
||||||
|
"indentStyle": "tab",
|
||||||
|
"indentWidth": 3,
|
||||||
|
"lineWidth": 120
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"includes": ["src/**/*.ts", "test/**/*.ts", "vitest.config.ts", "!**/node_modules/**/*", "!**/dist/**/*"]
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"includes": ["vitest.config.ts"],
|
||||||
|
"linter": {
|
||||||
|
"rules": {
|
||||||
|
"style": {
|
||||||
|
"noDefaultExport": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"name": "@code-yeongyu/codex-telemetry",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Codex plugin component that emits omo-codex anonymous daily-active telemetry on SessionStart.",
|
||||||
|
"type": "module",
|
||||||
|
"packageManager": "npm@11.12.1",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://github.com/code-yeongyu/oh-my-openagent",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/code-yeongyu/oh-my-openagent.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/code-yeongyu/oh-my-openagent/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"codex",
|
||||||
|
"codex-plugin",
|
||||||
|
"omo",
|
||||||
|
"telemetry",
|
||||||
|
"posthog",
|
||||||
|
"hooks",
|
||||||
|
"daily-active"
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"omo-telemetry": "./dist/cli.js"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"hooks",
|
||||||
|
"LICENSE",
|
||||||
|
"README.md",
|
||||||
|
"CHANGELOG.md"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc -p tsconfig.build.json",
|
||||||
|
"test": "vitest --run",
|
||||||
|
"test:watch": "vitest",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"lint": "biome check .",
|
||||||
|
"lint:fix": "biome check --write .",
|
||||||
|
"check": "tsc --noEmit && biome check . && npm run build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"posthog-node": "^5.34.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "2.4.15",
|
||||||
|
"@types/node": "^25.7.0",
|
||||||
|
"typescript": "^6.0.3",
|
||||||
|
"vitest": "^4.1.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowImportingTsExtensions": false,
|
||||||
|
"declaration": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"noEmit": false
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["test/**/*"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "Node16",
|
||||||
|
"moduleResolution": "Node16",
|
||||||
|
"lib": ["ES2022"],
|
||||||
|
"strict": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"useDefineForClassFields": false,
|
||||||
|
"types": ["node"],
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*", "test/**/*"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
environment: "node",
|
||||||
|
pool: "threads",
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user