tledger 0.1.2 → 0.1.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 jskoiz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -5,17 +5,31 @@ usage. It ranks projects, shows model and cache mix, and adds reset-cycle
5
5
  context without sending usage data anywhere. Codex Auto Review is shown
6
6
  separately with its token total, distinct-turn share, and cached-input share.
7
7
 
8
+ Token Ledger reads Codex history stored on the computer where it runs. It does
9
+ not sign in, fetch account-wide usage, or combine activity from other machines.
10
+
8
11
  ![Token Ledger running with synthetic demo data](https://raw.githubusercontent.com/jskoiz/token-ledger/main/docs/token-ledger-demo.svg)
9
12
 
10
13
  _The screenshot is generated from an intentionally synthetic fixture._
11
14
 
15
+ ## Requirements
16
+
17
+ Token Ledger requires Node.js 22.13 or newer and npm. Install a supported LTS
18
+ release from [nodejs.org](https://nodejs.org/en/download) if either command is
19
+ missing, then confirm the versions in a new terminal:
20
+
21
+ ```bash
22
+ node --version
23
+ npm --version
24
+ ```
25
+
12
26
  ## Install
13
27
 
14
- Token Ledger requires Node.js 22.13 or newer and has no runtime npm
15
- dependencies.
28
+ Token Ledger has no runtime npm dependencies and runs no installation script.
16
29
 
17
30
  ```bash
18
31
  npm install --global tledger
32
+ tledger --help
19
33
  ```
20
34
 
21
35
  ## Use
@@ -30,7 +44,8 @@ tledger week 2026-08-05
30
44
 
31
45
  The local timezone and top 10 projects are selected automatically. The default
32
46
  view is interactive in a terminal; use arrow keys or `j`/`k` to move and `q` or
33
- Escape to exit.
47
+ Escape to exit. If the selected period is empty, Token Ledger reports the most
48
+ recent local activity date and prints the exact command for opening it.
34
49
 
35
50
  Useful options:
36
51
 
@@ -51,6 +66,20 @@ Useful options:
51
66
  --help Show complete CLI help
52
67
  ```
53
68
 
69
+ ## Update or uninstall
70
+
71
+ ```bash
72
+ npm install --global tledger@latest
73
+ npm uninstall --global tledger
74
+ ```
75
+
76
+ Uninstalling the npm package leaves the privacy-reduced cache at
77
+ `~/.token-ledger/token-ledger-snapshot.json`. Remove that directory separately
78
+ only when you also want the next installation to perform a completely fresh
79
+ scan.
80
+
81
+ ## Local data and privacy
82
+
54
83
  By default, Token Ledger reads `CODEX_HOME` or `~/.codex` and keeps a
55
84
  privacy-reduced cache at `~/.token-ledger/token-ledger-snapshot.json`. It checks
56
85
  source freshness automatically. Fresh scans use a bounded pool of up to four
@@ -68,3 +97,7 @@ npm test
68
97
  npm run demo
69
98
  npm pack --dry-run
70
99
  ```
100
+
101
+ ## License
102
+
103
+ [MIT](LICENSE)
@@ -382,6 +382,48 @@ function sourceLabel(snapshotPath, snapshot) {
382
382
  return `${cleanLabel(basename(snapshotPath), "snapshot")} · captured ${generated}`;
383
383
  }
384
384
 
385
+ function latestActivityDateString(snapshot, timeZone) {
386
+ let latestTimestamp = Number.NEGATIVE_INFINITY;
387
+ for (const event of snapshot.events ?? []) {
388
+ const timestamp = new Date(event.timestamp).getTime();
389
+ if (Number.isFinite(timestamp) && timestamp > latestTimestamp) {
390
+ latestTimestamp = timestamp;
391
+ }
392
+ }
393
+ if (!Number.isFinite(latestTimestamp)) return null;
394
+ return dateStringFromParts(numericDateParts({
395
+ value: new Date(latestTimestamp),
396
+ timeZone,
397
+ }));
398
+ }
399
+
400
+ function displayCalendarDate(dateString) {
401
+ const [year, month, day] = dateString.split("-").map(Number);
402
+ return new Intl.DateTimeFormat("en-US", {
403
+ month: "long",
404
+ day: "numeric",
405
+ year: "numeric",
406
+ timeZone: "UTC",
407
+ }).format(new Date(Date.UTC(year, month - 1, day)));
408
+ }
409
+
410
+ function emptyState(options, snapshot, bounds) {
411
+ const rangeDescription = options.range === "week"
412
+ ? `${bounds.startDateString} through ${bounds.endDateString}`
413
+ : bounds.dateString;
414
+ const lines = [
415
+ `No model-call events found for ${rangeDescription} (${bounds.timeZone}).`,
416
+ "Token Ledger reads only Codex history stored on this computer.",
417
+ ];
418
+ const latestDate = latestActivityDateString(snapshot, bounds.timeZone);
419
+ if (latestDate) {
420
+ lines.push(`Latest local activity: ${displayCalendarDate(latestDate)}.`);
421
+ lines.push(`Try: tledger ${options.range} ${latestDate}`);
422
+ }
423
+ lines.push(`Source: ${sourceLabel(options.input, snapshot)}`);
424
+ return lines.join("\n");
425
+ }
426
+
385
427
  async function readSnapshot(snapshotPath) {
386
428
  let parsed;
387
429
  try {
@@ -489,13 +531,7 @@ export async function run(options) {
489
531
  const snapshot = await loadSnapshot(options);
490
532
  const events = filterDayEvents(snapshot, bounds);
491
533
  if (events.length === 0) {
492
- const rangeDescription = options.range === "week"
493
- ? `${bounds.startDateString} through ${bounds.endDateString}`
494
- : bounds.dateString;
495
- return [
496
- `No model-call events found for ${rangeDescription} (${bounds.timeZone}).`,
497
- `Source: ${sourceLabel(options.input, snapshot)}`,
498
- ].join("\n");
534
+ return emptyState(options, snapshot, bounds);
499
535
  }
500
536
  const allRows = aggregateProjects(snapshot, events, options);
501
537
  const rows = allRows.slice(0, options.top);
@@ -519,14 +555,7 @@ async function runInteractive(options) {
519
555
  const snapshot = await loadSnapshot(options);
520
556
  const events = filterDayEvents(snapshot, bounds);
521
557
  if (events.length === 0) {
522
- const rangeDescription = options.range === "week"
523
- ? `${bounds.startDateString} through ${bounds.endDateString}`
524
- : bounds.dateString;
525
- process.stdout.write([
526
- `No model-call events found for ${rangeDescription} (${bounds.timeZone}).`,
527
- `Source: ${sourceLabel(options.input, snapshot)}`,
528
- "",
529
- ].join("\n"));
558
+ process.stdout.write(`${emptyState(options, snapshot, bounds)}\n`);
530
559
  return;
531
560
  }
532
561
  const allRows = aggregateProjects(snapshot, events, options);
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "tledger",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "A local-only terminal dashboard for Codex token usage",
5
+ "license": "MIT",
5
6
  "keywords": [
6
7
  "codex",
7
8
  "cli",
@@ -24,6 +25,7 @@
24
25
  "files": [
25
26
  "bin/",
26
27
  "lib/",
28
+ "LICENSE",
27
29
  "README.md"
28
30
  ],
29
31
  "bin": {