ai-spend-agent 0.9.11 → 0.9.12

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 CHANGED
@@ -1,58 +1,80 @@
1
- # ai-spend-agent
1
+ # ai-spend-agent — Tilden’s free AI spend CLI
2
2
 
3
- The full [aibill](https://github.com/futurastudio/ai-spend-agent) CLI.
3
+ Inspect Claude Code and Codex activity locally, see its estimated value at
4
+ published API rates, and identify what deserves a closer look. Optional provider
5
+ reports add billing evidence; estimates and reported costs stay separate.
4
6
 
5
7
  ```bash
6
- npx aibill init
7
- npx aibill statusline install # optional Claude Code cache-only status line
8
- npx ai-spend-agent
9
- # short alias
10
- npx aibill
8
+ npx aibill@latest init
9
+ npx aibill@latest --full
11
10
  ```
12
11
 
13
- Run `npx aibill init` from a project to detect machine-wide Claude Code, Codex,
14
- and experimental Gemini CLI financial evidence, print the first
15
- evidence-labeled personal receipt, and seed a private Claude/Codex aggregate
16
- cache under `~/.aibill/cache/`. Gemini financial rows come only from
17
- `~/.gemini/tmp/<opaque-project-id>/chats/**/*.{json,jsonl}`; `logs.json` is a
18
- presence signal and never a financial source. Init never replaces missing personal
19
- evidence with the bundled sample and never overwrites existing connected
20
- source or audit state.
21
-
22
- The optional status line is explicit and reversible. It installs a standalone
23
- Node-builtins-only runner at Claude user scope, rereads only the private
24
- aggregate cache, and never scans transcripts or contacts a provider from the
25
- hook. Subscription runway appears only when it was transcript-reported; `~`
26
- means API-equivalent value, and untilded `billed` money requires verified
27
- provider evidence. Remove it with `npx aibill statusline uninstall`.
28
-
29
- It reads supported local Claude Code, Codex, and Gemini CLI financial metadata,
30
- labels API-equivalent estimates, and can optionally add official OpenAI or
31
- Anthropic provider-reported cost
32
- through an environment-variable reference.
12
+ Requires Node.js 22+. Free and MIT-licensed; no account or provider key is
13
+ needed for local analysis. `aibill` and `ai-spend-agent` run the same CLI.
14
+ Run from a project directory: `init` reviews the last 30 days of supported
15
+ machine-wide records, uses that project for local state, and seeds the private
16
+ Claude Code/Codex status cache. It does not change Claude settings or connect
17
+ Workspace. Missing evidence stays missing; `--sample` explicitly selects a demo.
18
+
19
+ Local values are API-equivalent estimates, not subscription charges or invoices.
20
+ Claude Code/Codex readers are live-verified; Gemini CLI is experimental and
21
+ fixture-verified, supported only in financial CLI/report/local MCP surfaces.
22
+ Gemini does not feed statusline, Glance, Context Health, Apply or plan/runway.
23
+ `npx aibill@latest doctor --sources` shows reader validation separately from
24
+ each number’s financial label and coverage.
25
+
26
+ ## Choose a next step
27
+
28
+ - `npx aibill@latest context`: inspect context evidence.
29
+ - `npx aibill@latest improve`: guide one reversible token experiment; a result
30
+ requires matched completed sessions and user-declared quality, not a claim
31
+ of cash savings or ROI.
32
+ - `npx aibill@latest statusline install`: optionally install the cache-only
33
+ Claude Code status line. Remove it with `statusline uninstall`.
34
+ - `npx aibill@latest connect openai` or `connect anthropic`: register local
35
+ provider setup and print the separate `sync-provider` command. Only an
36
+ explicit sync reads the provider, using the required admin environment
37
+ credential reference. Cursor/Copilot connectors remain fixture-verified beta
38
+ pending real-account acceptance.
39
+
40
+ ## Use standalone or connect to Workspace
41
+
42
+ Use the free CLI on its own, or keep working locally and connect it to your
43
+ invited [Tilden Workspace](https://asktilden.com/docs/workspace) for a shared
44
+ spending review. Start with `npx aibill@latest workspace connect`, complete the
45
+ browser enrollment and sharing consent, then run
46
+ `npx aibill@latest workspace push` to preview and approve a share. The browser
47
+ does not need to stay open for later pushes while the machine’s grant and
48
+ sharing consent remain valid. Shared facts include eligible
49
+ Claude Code/Codex sessions, tokens and project labels where available, plus
50
+ hashed identities; they exclude raw prompts, transcript contents, full paths,
51
+ raw session IDs and dollar amounts. Local provider records are not uploaded.
52
+ Hosted provider connections are set up separately.
53
+
54
+ Read [Workspace setup and limits](https://github.com/futurastudio/ai-spend-agent/blob/main/docs/WORKSPACE.md)
55
+ before pairing. [Join the Tilden waitlist](https://asktilden.com/?ref=npm-cli#beta)
56
+ for updates and access invitations; joining does not grant immediate access.
57
+
58
+ ## Privacy
59
+
60
+ Default transcript analysis runs locally. Reports remain local until shared.
61
+ Explicit Workspace pushes send only the approved facts. Local MCP results go
62
+ to the AI client you configure and follow its data policy. Explicit provider
63
+ sync uses the referenced credential only with that provider’s read-only API;
33
64
  aibill never sits in the inference path and never stores, prints, or proxies provider credentials.
65
+ Workspace pairing uses separate private local device credentials.
66
+
67
+ **Telemetry: command counts, disclosed at first run.** Events begin
68
+ only after the first interactive notice. They include a random installation ID
69
+ and operational fields. Events can be linked to the same installation, but do
70
+ not include your arguments, paths, prompts, project names,
71
+ dollar amounts or email. `npx aibill telemetry off` disables them; so do
72
+ `DO_NOT_TRACK`, `CI` or `AI_SPEND_NO_TELEMETRY` when non-empty.
73
+ `npx aibill telemetry` shows the exact last payload.
74
+ [Full telemetry disclosure](https://github.com/futurastudio/ai-spend-agent/blob/main/docs/TELEMETRY.md).
75
+ The optional signup command sends its displayed email/ref payload only after
76
+ confirmation; `outcome github` explicitly uses the installed `gh` CLI.
34
77
 
35
- **Telemetry: anonymous command counts, disclosed at first run.** Terminal
36
- analysis stays on your machine — transcripts, prompts, file names, and dollar
37
- amounts are never uploaded. Separately, the CLI counts *which* commands run:
38
- anonymous, never your arguments, paths, content, dollars, or your email.
39
- Nothing is sent before a one-time notice has been printed on an interactive
40
- run, so a machine that has never seen that notice (CI, scripts, pipes) never
41
- sends anything. One command turns it off — `aibill telemetry off` (or
42
- `DO_NOT_TRACK`, `CI`, or `AI_SPEND_NO_TELEMETRY`) — `aibill telemetry` prints
43
- the exact last payload verbatim, and while it is on every receipt says so in
44
- place of "nothing uploaded". Full scope and the never-list:
45
- [`docs/TELEMETRY.md`](https://github.com/futurastudio/ai-spend-agent/blob/main/docs/TELEMETRY.md).
46
-
47
- Connector validation (`live_verified`, `fixture_verified`, `untested`, or
48
- `failed`) and each number's financial evidence (`verified`, `estimated`,
49
- `detected_unverified`, or `missing`) are separate status axes. Run `npx aibill
50
- doctor --sources` to see both with freshness and the last sanitized error.
51
- The source registry also records read-boundary approval separately; an approved
52
- local folder is permission to scan, not verified financial evidence.
53
-
54
- See the repository
55
- [README](https://github.com/futurastudio/ai-spend-agent#readme) for commands,
56
- privacy boundaries, supported sources, and public-beta limitations.
57
-
58
- MIT licensed.
78
+ [Full guide](https://github.com/futurastudio/ai-spend-agent#readme) ·
79
+ [Source coverage](https://asktilden.com/docs/sources) ·
80
+ [Command reference](https://github.com/futurastudio/ai-spend-agent/blob/main/docs/CLI_REFERENCE.md)
package/dist/index.js CHANGED
@@ -592,7 +592,7 @@ function quickstartNextSteps(mode, detected) {
592
592
  command: "npx aibill --group-by project",
593
593
  description: "see which project has the most observed activity"
594
594
  });
595
- steps.push("Need team reconciliation, allocation, budgets, and approvals? Workspace design partners: https://asktilden.com");
595
+ steps.push("Review supported AI spend in Tilden Workspace with our team’s help. The pilot has one owner seat. CLI sharing is optional and explicit; register interest: https://asktilden.com/?ref=aibill-workspace#beta");
596
596
  if (mode === "demo") {
597
597
  // Static pointer only — sample output is built for recordings and
598
598
  // screenshots, so it never prompts (capture design moments map).
@@ -1256,7 +1256,7 @@ async function signupCommand(args, runtime) {
1256
1256
  };
1257
1257
  }
1258
1258
  /**
1259
- * `aibill telemetry [on|off]` — inspect or switch anonymous command-count
1259
+ * `aibill telemetry [on|off]` — inspect or switch installation-linked command-count
1260
1260
  * telemetry. Status shows the EXACT last payload verbatim; state is
1261
1261
  * fail-closed (corrupt/readonly ⇒ off).
1262
1262
  */
@@ -1315,8 +1315,8 @@ async function telemetryCommand(args, runtime) {
1315
1315
  };
1316
1316
  }
1317
1317
  return ok([
1318
- "telemetry on · anonymous command counts only",
1319
- `counted: command name, version, os, arch, ci flag, duration bucket, ok flag, timestamp`,
1318
+ "telemetry on · installation-linked command counts only",
1319
+ `counted: random installation ID, command name, version, os, arch, ci flag, duration bucket, ok flag, timestamp`,
1320
1320
  "never: arguments, paths, file contents, project names, or your email",
1321
1321
  "events start with your next run · see payloads anytime: npx aibill telemetry"
1322
1322
  ].join("\n"));
@@ -1338,12 +1338,12 @@ async function telemetryCommand(args, runtime) {
1338
1338
  else {
1339
1339
  lines.push(`status: on · noticed ${read.state.noticedAt}`);
1340
1340
  }
1341
- lines.push("counted: command name, version, os, arch, ci flag, duration bucket, ok flag, timestamp", "never: arguments, paths, file contents, project names, or your email");
1341
+ lines.push("counted: random installation ID, command name, version, os, arch, ci flag, duration bucket, ok flag, timestamp", "never: arguments, paths, file contents, project names, or your email");
1342
1342
  if (read.kind === "ok" && read.state.lastPayload !== undefined) {
1343
- lines.push("last payload sent (verbatim):", read.state.lastPayload);
1343
+ lines.push("last attempted payload (verbatim; delivery unconfirmed):", read.state.lastPayload);
1344
1344
  }
1345
1345
  else {
1346
- lines.push("last payload sent: none");
1346
+ lines.push("last attempted payload: none");
1347
1347
  }
1348
1348
  lines.push("switch: npx aibill telemetry on · npx aibill telemetry off");
1349
1349
  return ok(lines.join("\n"));
@@ -3401,10 +3401,10 @@ const providerAdminEnvHint = {
3401
3401
  };
3402
3402
  function providerSyncSetupCommand(provider, adminRef) {
3403
3403
  if (provider === "cursor") {
3404
- return `npx aibill sync-provider --provider cursor --auth-reference ${adminRef} --account-id <team-label>`;
3404
+ return `npx aibill sync-provider --provider cursor --auth-reference ${adminRef} --account-id '<team-label>'`;
3405
3405
  }
3406
3406
  if (provider === "github-copilot") {
3407
- return `npx aibill sync-provider --provider github-copilot --auth-reference ${adminRef} --org <organization>`;
3407
+ return `npx aibill sync-provider --provider github-copilot --auth-reference ${adminRef} --org '<organization>'`;
3408
3408
  }
3409
3409
  const thirtyDaysAgoUnix = Math.floor(Date.now() / 1_000) - 30 * 24 * 60 * 60;
3410
3410
  return `npx aibill sync-provider --provider ${provider} --auth-reference ${adminRef} --start-time ${thirtyDaysAgoUnix}`;
@@ -7461,7 +7461,7 @@ function helpText(telemetryDisclosure) {
7461
7461
  " statusline expand Print every subscription with committed price, runways, and 7d API-equivalent",
7462
7462
  " signup <email> [--ref <token>] Join the launch list · email only · the exact payload is shown before send",
7463
7463
  " [--never] Never ask again (nothing is sent) [--forget] Clear local signup state",
7464
- " telemetry [on|off] Show anonymous command-count status + the exact last payload · switch it",
7464
+ " telemetry [on|off] Show installation-linked command-count status + the exact last payload · switch it",
7465
7465
  " doctor [--sources] Launch diagnostics; --sources shows validation, evidence, freshness, and errors",
7466
7466
  " reset [--path <dir>] Clear persisted spend state (so sample state can't mask real logs)",
7467
7467
  " --ignore-state On the default/quickstart run, ignore persisted spend.json for this run",
@@ -1,6 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  /**
3
- * CLI telemetry — anonymous command counts, notice-before-first-byte.
3
+ * CLI telemetry — installation-linked command counts, notice-before-first-byte.
4
4
  *
5
5
  * Consent model (founder decision 2026-08-24, disclosed opt-out):
6
6
  * - Run 1 (interactive) prints a three-line notice AFTER the output and
@@ -55,7 +55,7 @@ export type TelemetryEvent = {
55
55
  * incident — "command not found").
56
56
  */
57
57
  export declare const telemetryDisclosureLine: string;
58
- export declare const telemetryNoticeLines: readonly ["aibill counts which commands run — anonymous, never your data or content", `turn off: ${string}`, `see payloads: ${string}`];
58
+ export declare const telemetryNoticeLines: readonly ["aibill counts commands by random installation ID — no arguments, paths or content", `turn off: ${string}`, `see payloads: ${string}`];
59
59
  export declare function telemetryOsLabel(platform?: string): TelemetryOs;
60
60
  export declare function telemetryArchLabel(arch?: string): TelemetryArch;
61
61
  export declare function telemetryDurationBucket(durationMs: number): TelemetryDurationBucket;
package/dist/telemetry.js CHANGED
@@ -5,7 +5,7 @@ import { homedir } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
6
  import { aibillCommandV0 } from "@agent-finops/core";
7
7
  /**
8
- * CLI telemetry — anonymous command counts, notice-before-first-byte.
8
+ * CLI telemetry — installation-linked command counts, notice-before-first-byte.
9
9
  *
10
10
  * Consent model (founder decision 2026-08-24, disclosed opt-out):
11
11
  * - Run 1 (interactive) prints a three-line notice AFTER the output and
@@ -69,9 +69,9 @@ export const telemetryCommands = [
69
69
  * npx form): npx users have no bare `aibill` on PATH (0.9.2 founder
70
70
  * incident — "command not found").
71
71
  */
72
- export const telemetryDisclosureLine = `anonymous command counts shared · ${aibillCommandV0("telemetry off")}`;
72
+ export const telemetryDisclosureLine = `installation-linked command counts shared · ${aibillCommandV0("telemetry off")}`;
73
73
  export const telemetryNoticeLines = [
74
- "aibill counts which commands run — anonymous, never your data or content",
74
+ "aibill counts commands by random installation ID — no arguments, paths or content",
75
75
  `turn off: ${aibillCommandV0("telemetry off")}`,
76
76
  `see payloads: ${aibillCommandV0("telemetry")}`
77
77
  ];
@@ -318,7 +318,7 @@ export async function openCliTelemetry(options = {}) {
318
318
  return;
319
319
  // `glance` is a machine-invoked poll (the Glance menu-bar app spawns
320
320
  // it every ~30s), not a human command — counting it is noise by
321
- // definition and would flood the anonymous command counts (~2,880
321
+ // definition and would flood the installation-linked command counts (~2,880
322
322
  // events/day/user). Never emit for it, notice or not; the label
323
323
  // still exists in the map so any stray event is at least honest.
324
324
  if (telemetryCommandForArgv(input.argv) === "glance")
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "ai-spend-agent",
3
- "version": "0.9.11",
3
+ "version": "0.9.12",
4
4
  "funding": "https://asktilden.com",
5
- "description": "Local-first financial accountability CLI: Claude Code/Codex attribution, provenance, and next actions, plus experimental Gemini CLI cost evidence.",
5
+ "description": "Tilden\u2019s free CLI for local coding-agent usage and optional provider cost reports. Estimates, reported costs and missing coverage stay separate.",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
@@ -55,8 +55,8 @@
55
55
  "prepack": "npm run build"
56
56
  },
57
57
  "dependencies": {
58
- "@agent-finops/core": "0.9.11",
59
- "@agent-finops/report": "0.9.11",
58
+ "@agent-finops/core": "0.9.12",
59
+ "@agent-finops/report": "0.9.12",
60
60
  "yocto-spinner": "^1.2.0"
61
61
  }
62
62
  }