@marginfront/code-cost-clarity 0.5.4 → 0.6.1

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.
Files changed (4) hide show
  1. package/README.md +62 -21
  2. package/dist/cli.d.ts +11 -2
  3. package/dist/cli.js +1848 -1636
  4. package/package.json +2 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > See your Claude Code **and Codex** spend in MarginFront. One command wires your
4
4
  > coding-agent usage telemetry through a local collector and into MarginFront,
5
- > priced per engineer, per model, with **accurate prompt-cache token splitting**.
5
+ > priced per developer, per model, with **accurate prompt-cache token splitting**.
6
6
  > One package, three modes: Claude Code only, Codex only, or both.
7
7
 
8
8
  > **⚠️ Beta (pilot software).** Under active development - pin a version, expect rough
@@ -17,8 +17,8 @@
17
17
  **📖 Full documentation:** https://docs.marginfront.com/tools/code-cost-clarity
18
18
 
19
19
  **This is internal cost visibility, not billing and not a spend cap.** The
20
- installing company watches its own AI coding spend (per engineer, per model,
21
- R&D-vs-COGS). It does not charge engineers and it does not cut anyone off.
20
+ installing company watches its own AI coding spend (per developer, per model,
21
+ R&D-vs-COGS). It does not charge developers and it does not cut anyone off.
22
22
 
23
23
  **Your LLM keys stay yours.** This tool never reads, needs, or transmits your
24
24
  Anthropic or OpenAI API key. The only credential it uses is your MarginFront key
@@ -28,7 +28,7 @@ Anthropic or OpenAI API key. The only credential it uses is your MarginFront key
28
28
 
29
29
  ## What this does (one sentence)
30
30
 
31
- Every time an engineer runs Claude Code, this connector captures the token usage
31
+ Every time a developer runs Claude Code, this connector captures the token usage
32
32
  and sends it to MarginFront as a usage event, so you can see who used what, on
33
33
  which model, and how much it cost.
34
34
 
@@ -37,13 +37,13 @@ which model, and how much it cost.
37
37
  Think of it like a cash-register receipt system:
38
38
 
39
39
  1. **Claude Code** is the register. As it works, it broadcasts receipts
40
- (OpenTelemetry exports): how many tokens, which model, which engineer.
40
+ (OpenTelemetry exports): how many tokens, which model, which developer.
41
41
  2. **The collector** (`otelcol-contrib`, open source) is the catcher. It runs in
42
42
  the background, catches the receipts, and writes them to a file.
43
43
  3. **The forwarder** (our glue, inside this package) reads those receipts and
44
44
  sends each one to MarginFront.
45
45
  4. **MarginFront** records the event, prices it, and shows it under the
46
- engineer's email.
46
+ developer's email.
47
47
 
48
48
  ```
49
49
  Claude Code (your task)
@@ -103,7 +103,7 @@ claude # or: codex (or open the Claude/Codex desktop apps)
103
103
  > config when they launch, so anything that was already open won't emit until it's reopened.
104
104
 
105
105
  That's it. No `source`, no second terminal. Your spend shows up in MarginFront under
106
- your engineer email, automatically, and the background meter restarts itself at
106
+ your developer email, automatically, and the background meter restarts itself at
107
107
  every login. Check on it anytime:
108
108
 
109
109
  ```bash
@@ -115,7 +115,7 @@ npx @marginfront/code-cost-clarity status
115
115
  stops it). You'll see a line per turn like:
116
116
 
117
117
  ```
118
- [14:22:07] recorded engineer@example.com · in=3210 out=287 · server=$0.0232 · cc=$0.0236 event=9f0c2a71-...
118
+ [14:22:07] recorded developer@example.com · in=3210 out=287 · server=$0.0232 · cc=$0.0236 event=9f0c2a71-...
119
119
  ```
120
120
 
121
121
  **CI / scripted installs:** add `--no-prompt` to `init` to skip BOTH questions (the
@@ -180,34 +180,75 @@ It does **not** capture:
180
180
 
181
181
  ---
182
182
 
183
- ## Per-engineer attribution is automatic
183
+ ## Per-developer attribution is automatic
184
184
 
185
185
  What makes "who spent what" work is `user.email`, and **Claude Code puts it in
186
- the telemetry on its own**. It's the engineer's logged-in Claude account email.
187
- There is no manual email config. Each engineer loads the telemetry settings
186
+ the telemetry on its own**. It's the developer's logged-in Claude account email.
187
+ There is no manual email config. Each developer loads the telemetry settings
188
188
  and runs Claude Code normally.
189
189
 
190
- **Works whether the engineer signs in with an org-managed seat or an interactive
190
+ **Works whether the developer signs in with an org-managed seat or an interactive
191
191
  login.** On org-managed Claude seats the email is stamped for free. If an
192
- engineer's sign-in doesn't surface an email, the connector doesn't drop the
192
+ developer's sign-in doesn't surface an email, the connector doesn't drop the
193
193
  usage. It attributes it to a clearly labeled placeholder customer
194
194
  (`claude-code-no-identity`) and prints how to fix it (sign in with an org-managed
195
195
  seat, or attach a customer mapping). You'll see the placeholder in `preview`/`run`
196
196
  output if it ever kicks in.
197
197
 
198
- > The MarginFront **API key** is separate from the engineer's identity: it's the
198
+ > The MarginFront **API key** is separate from the developer's identity: it's the
199
199
  > connector's own credential for posting to MarginFront. `run` needs it;
200
200
  > `preview` does not.
201
201
 
202
202
  ---
203
203
 
204
+ ## Shared one login across the team? Set a per-machine developer (`CCC_DEVELOPER_EMAIL`)
205
+
206
+ Some teams share **one** Claude/Codex login across the whole team. When that
207
+ happens, every developer's telemetry carries the **same** `user.email`, so all the
208
+ AI cost collapses onto one person and per-developer attribution breaks. CCC fixes
209
+ this without any change to how you log in, because **CCC runs locally on each
210
+ laptop** - so each laptop can carry its own developer identity.
211
+
212
+ **During `init` we ask one question:** which developer this machine's AI cost should
213
+ be attributed to. The prompt is pre-filled with your **global git email**
214
+ (`git config --global user.email`), which is usually the right person sitting at
215
+ this laptop. You have three choices:
216
+
217
+ - **Press Enter** to accept the pre-filled email.
218
+ - **Type a different email** to attribute this machine to someone else.
219
+ - **Clear it and leave it blank** to keep the old behavior - auto-detect from
220
+ whatever email the coding-agent login reports.
221
+
222
+ Whatever you choose is saved as **`CCC_DEVELOPER_EMAIL`** in
223
+ `~/.marginfront-ccc/.env` (mode 600, right next to your key). To change it later,
224
+ edit that one line and then `stop` + `start` (or just re-run `init`).
225
+
226
+ **Precedence at send time (highest wins):**
227
+
228
+ 1. **`CCC_DEVELOPER_EMAIL`** (this machine's developer) - if set, it wins for **every**
229
+ record this machine sends, Claude **and** Codex, token turns **and** tool calls.
230
+ 2. Otherwise the email the coding-agent login reports (`user.email`).
231
+ 3. Otherwise the no-identity placeholder (`claude-code-no-identity` /
232
+ `codex-no-identity`).
233
+
234
+ If you **don't** set it (leave it blank), nothing changes - attribution is
235
+ byte-for-byte what it was before this feature: the login's own `user.email`, then
236
+ the placeholder.
237
+
238
+ > This is **internal cost visibility only**. `CCC_DEVELOPER_EMAIL` re-labels which
239
+ > developer the cost shows up under in _your_ MarginFront - it does **not** change
240
+ > who pays or any customer's bill. The `--no-prompt` (CI / scripted) install skips
241
+ > this question and leaves `CCC_DEVELOPER_EMAIL` unset.
242
+
243
+ ---
244
+
204
245
  ## Privacy (read this)
205
246
 
206
247
  This tool is **internal cost visibility**, and being honest about what it turns on
207
248
  matters, so here is the full picture in plain English.
208
249
 
209
- **Your engineer email travels in the telemetry, in plaintext, on purpose.** That's
210
- the whole point: per-engineer cost attribution needs to know who ran the turn.
250
+ **Your developer email travels in the telemetry, in plaintext, on purpose.** That's
251
+ the whole point: per-developer cost attribution needs to know who ran the turn.
211
252
  Claude Code stamps `user.email` (your logged-in account email) onto the usage
212
253
  telemetry, the local collector writes it to a file on your machine, and the
213
254
  forwarder POSTs it to MarginFront so the spend lands under your name. It is **not**
@@ -304,7 +345,7 @@ silent charge). Free built-ins (file reads, shell, grep) are never forwarded.
304
345
 
305
346
  The same package captures **Codex** too. Nothing extra to install. Codex reports
306
347
  to the **same** local collector. You get three modes for free: Claude Code only,
307
- Codex only, or **both** (same engineer's name on all of it).
348
+ Codex only, or **both** (same developer's name on all of it).
308
349
 
309
350
  **`init` turns it on for you** when you consent, it adds this `[otel]` block to
310
351
  your `~/.codex/config.toml` automatically:
@@ -350,8 +391,8 @@ the input rate and drops the cache-read field, so the number reads high, never l
350
391
 
351
392
  ### Identity by sign-in mode (org seat vs ChatGPT login)
352
393
 
353
- Per-engineer attribution rides on `user.email` from Codex's telemetry, same idea
354
- as Claude Code. Whether that email surfaces depends on how the engineer signs in
394
+ Per-developer attribution rides on `user.email` from Codex's telemetry, same idea
395
+ as Claude Code. Whether that email surfaces depends on how the developer signs in
355
396
  to Codex (an org-managed or API-key sign-in stamps it; some interactive ChatGPT
356
397
  logins may not). We never read that OpenAI key; the only thing that matters here is
357
398
  whether the telemetry carries an email. If it doesn't surface, the usage isn't
@@ -375,7 +416,7 @@ exactly like the Claude path.
375
416
 
376
417
  > **Confirm the dollar figure once.** The exact Codex dollar figure should be
377
418
  > confirmed against one real captured session: that Codex reports per-turn (not
378
- > session-cumulative) token counts on the log stream, and that your engineer email
419
+ > session-cumulative) token counts on the log stream, and that your developer email
379
420
  > populates under your sign-in mode. The mapping above is the verified-safe
380
421
  > default; the confirmation is a one-session spike, not a blocker to installing.
381
422
 
@@ -454,7 +495,7 @@ curl -s -H "x-api-key: $KEY" \
454
495
 
455
496
  ---
456
497
 
457
- ## For engineers (technical appendix)
498
+ ## For developers (technical appendix)
458
499
 
459
500
  **Input shape:** OTLP/JSON, tree `resourceMetrics[].scopeMetrics[].metrics[]`. Two
460
501
  metrics matter: `claude_code.token.usage` (one datapoint per `type` in
package/dist/cli.d.ts CHANGED
@@ -33,6 +33,7 @@ interface PromptIO {
33
33
  terminal?: boolean;
34
34
  }
35
35
  declare function promptYesNo(promptText: string, io?: PromptIO, defaultYes?: boolean): Promise<boolean>;
36
+ declare function promptVisible(promptText: string, defaultValue?: string, io?: PromptIO): Promise<string>;
36
37
  interface InitDeps {
37
38
  ensureConfigFiles?: () => void;
38
39
  ensureCollectorBinary?: () => void;
@@ -40,6 +41,9 @@ interface InitDeps {
40
41
  isTTY?: () => boolean;
41
42
  promptSecret?: (promptText: string) => Promise<string>;
42
43
  writeApiKey?: (value: string) => void;
44
+ gitDefaultEmail?: () => string;
45
+ promptDeveloperEmail?: (defaultValue: string) => Promise<string>;
46
+ writeDeveloperEmail?: (value: string) => void;
43
47
  hasApiKey?: () => boolean;
44
48
  promptConsent?: () => Promise<boolean>;
45
49
  writeClaudeTelemetry?: () => WriteClaudeSettingsResult;
@@ -83,8 +87,13 @@ interface UninstallDeps {
83
87
  purgeConfigDir?: () => void;
84
88
  findZshrcLines?: () => ZshrcSourceLineResult;
85
89
  unsetDesktopEnv?: () => void;
90
+ readForwarderPid?: () => number | null;
91
+ isForwarderAlive?: (pid: number) => boolean;
92
+ signalForwarder?: (pid: number, signal: "SIGTERM" | "SIGKILL") => void;
93
+ sleep?: (ms: number) => Promise<void>;
94
+ now?: () => number;
86
95
  log?: (message: string) => void;
87
96
  }
88
- declare function cmdUninstall(purge: boolean, deps?: UninstallDeps): number;
97
+ declare function cmdUninstall(purge: boolean, deps?: UninstallDeps): Promise<number>;
89
98
 
90
- export { type InitDeps, type PromptIO, type StartDeps, type StopDeps, type UninstallDeps, cmdInit, cmdRun, cmdStart, cmdUninstall, promptYesNo, runningForwarderPid, stopForwarderThenStopCollector };
99
+ export { type InitDeps, type PromptIO, type StartDeps, type StopDeps, type UninstallDeps, cmdInit, cmdRun, cmdStart, cmdUninstall, promptVisible, promptYesNo, runningForwarderPid, stopForwarderThenStopCollector };