@traice/codex-collector 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # Internal Spend Codex Collector
2
+
3
+ This installs a local bridge from Codex developer-agent usage into trAIce
4
+ Internal Spend.
5
+
6
+ Internal Spend is intentionally separate from product-runtime LLM attribution:
7
+ collector data goes to `/api/v1/internal-usage`, not `/api/v1/events`.
8
+
9
+ ## What It Collects
10
+
11
+ - Codex token-count rows from `~/.codex/sessions/**/*.jsonl`
12
+ - Codex OTel `response.completed` token events from `127.0.0.1:4318/v1/logs`
13
+ - local install identity: employee email/name, team name, device principal
14
+ - Codex context metadata: repo/cwd, model, thread id, sandbox/approval mode
15
+ - optional monthly seat/subscription commitment, e.g. `--seat-monthly-usd 200`
16
+
17
+ Prompts are not sent. Codex OTel is configured with `log_user_prompt = false`.
18
+
19
+ ## Install On This Device
20
+
21
+ Published install shape:
22
+
23
+ ```sh
24
+ read -rsp "trAIce API key: " TRAICE_API_KEY; echo
25
+ export TRAICE_API_KEY
26
+ npx @traice/codex-collector@latest install \
27
+ --server-url https://app.runtraice.com \
28
+ --employee-email you@company.com \
29
+ --employee-name "Your Name" \
30
+ --team-name Engineering \
31
+ --seat-monthly-usd 200
32
+ unset TRAICE_API_KEY
33
+ ```
34
+
35
+ Local repo development can still run:
36
+
37
+ ```sh
38
+ node tools/internal-spend-codex/install.mjs \
39
+ --employee-email you@company.com \
40
+ --employee-name "Your Name" \
41
+ --team-name Engineering \
42
+ --seat-monthly-usd 200
43
+ ```
44
+
45
+ The installer:
46
+
47
+ - creates or reuses the `internal-spend-poc` workspace in local dev
48
+ - enables the workspace `internalSpendEnabled` flag
49
+ - creates a real API key for `/api/v1/internal-usage`
50
+ - writes private collector config to `~/.traice/internal-spend-codex/install.json`
51
+ - appends an idempotent trAIce `[otel]` block to user-level
52
+ `~/.codex/config.toml`
53
+
54
+ Codex ignores telemetry settings in project `.codex/config.toml`, so the user
55
+ config patch is intentional.
56
+
57
+ ## Start The Collector
58
+
59
+ Foreground:
60
+
61
+ ```sh
62
+ npx @traice/codex-collector@latest collect
63
+ ```
64
+
65
+ Local repo development can use `node tools/internal-spend-codex/collector.mjs`.
66
+
67
+ macOS LaunchAgent:
68
+
69
+ ```sh
70
+ read -rsp "trAIce API key: " TRAICE_API_KEY; echo
71
+ export TRAICE_API_KEY
72
+ npx @traice/codex-collector@latest install \
73
+ --server-url https://app.runtraice.com \
74
+ --employee-email you@company.com \
75
+ --team-name Engineering \
76
+ --seat-monthly-usd 200 \
77
+ --launch-agent
78
+ unset TRAICE_API_KEY
79
+ ```
80
+
81
+ The collector listens on:
82
+
83
+ ```text
84
+ http://127.0.0.1:4318/v1/logs
85
+ ```
86
+
87
+ and forwards normalized internal usage rows to:
88
+
89
+ ```text
90
+ http://127.0.0.1:3003/api/v1/internal-usage
91
+ ```
92
+
93
+ View the module at:
94
+
95
+ ```text
96
+ http://127.0.0.1:3003/dashboard?tab=internal
97
+ ```
98
+
99
+ ## Employee And Team Mapping
100
+
101
+ For this local collector, identity comes from install flags:
102
+
103
+ - `--employee-email`
104
+ - `--employee-name`
105
+ - `--team-name`
106
+ - `--source-principal`
107
+ - `--seat-monthly-usd`
108
+
109
+ If omitted, the installer falls back to global git identity and local OS user.
110
+ Future org-wide installs should feed the same fields from MDM/SSO/directory
111
+ data. CSV and vendor connectors should use the same `/api/v1/internal-usage`
112
+ contract and fill employee/team fields directly or via identity mapping.
113
+
114
+ The API key can be provided with `TRAICE_API_KEY`, `--api-key-stdin`, or
115
+ `--api-key`. Prefer `TRAICE_API_KEY` or `--api-key-stdin` for user installs so
116
+ keys do not end up in shell history.
117
+
118
+ Private config and collector state default to `~/.traice/internal-spend-codex`.
119
+ Use `--out /path/to/private-dir` only when an org installer manages that
120
+ directory and permissions explicitly.
121
+
122
+ `--seat-monthly-usd` records a monthly subscription commitment for the
123
+ employee/source pair. It is shown separately from per-request usage cost because
124
+ Codex token logs do not include provider invoice amounts.
125
+
126
+ ## Current Open Codex Agents
127
+
128
+ Existing Codex processes usually will not reload new user-level OTel config in
129
+ place. To cover already-open agents, the collector tails
130
+ `~/.codex/sessions/**/*.jsonl` and ingests `token_count` events appended by
131
+ running Codex sessions.
132
+
133
+ New or restarted Codex agents use native OTel export through the installed
134
+ `[otel]` config. Already-open agents are covered by session JSONL token-count
135
+ tailing where those files are available.
136
+
137
+ ## One-Shot Backfill
138
+
139
+ Run a single session-file scan without opening the OTel listener:
140
+
141
+ ```sh
142
+ npx @traice/codex-collector@latest collect --once
143
+ ```
144
+
145
+ The collector keeps local dedupe state in:
146
+
147
+ ```text
148
+ ~/.traice/internal-spend-codex/collector-state.json
149
+ ```
150
+
151
+ so restarting it should not intentionally duplicate historical rows.
package/cli.mjs ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const dir = dirname(fileURLToPath(import.meta.url));
7
+ const [command = 'collect', ...args] = process.argv.slice(2);
8
+ const script = command === 'install'
9
+ ? 'install.mjs'
10
+ : command === 'collect' || command === 'collector'
11
+ ? 'collector.mjs'
12
+ : null;
13
+
14
+ if (!script) {
15
+ console.error('Usage: traice-codex-collector <install|collect> [options]');
16
+ process.exit(2);
17
+ }
18
+
19
+ const result = spawnSync(process.execPath, [resolve(dir, script), ...args], {
20
+ stdio: 'inherit',
21
+ });
22
+
23
+ if (result.signal) {
24
+ process.kill(process.pid, result.signal);
25
+ }
26
+ process.exit(result.status ?? 1);