savemytokens 0.2.2 → 0.2.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/README.md +3 -3
- package/dist/report/views.js +1 -1
- package/dist/runtime/kernel.mjs +19 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -191,9 +191,9 @@ bar the built-in themes have to clear.
|
|
|
191
191
|
beyond those two facts would be a guess dressed up as a number.
|
|
192
192
|
- **It cannot see usage it did not measure.** Another machine, claude.ai, or work done before you
|
|
193
193
|
installed it shows up as unattributed window, labelled as such.
|
|
194
|
-
- **
|
|
195
|
-
|
|
196
|
-
|
|
194
|
+
- **The split moves under you.** A window that has taken no turn in ten minutes lends its share to
|
|
195
|
+
the rest, and takes it back the moment you type in it again. What is on screen is the current
|
|
196
|
+
split, not a contract: a project's allocation can change without you touching anything.
|
|
197
197
|
- **It makes no network call.** No account, no telemetry, no daemon. `savemytokens privacy` prints
|
|
198
198
|
every file it reads and writes.
|
|
199
199
|
|
package/dist/report/views.js
CHANGED
|
@@ -419,7 +419,7 @@ export function helpOverlay(control, context) {
|
|
|
419
419
|
out.push(...helpSection("the status line", context));
|
|
420
420
|
out.push(...helpProse(control.installed
|
|
421
421
|
? "Installed. It is the only place your 5h and 7d usage is published, and it is what proves a session is still open, so without it nothing here is live. Change its shape with P."
|
|
422
|
-
: "Not installed, so nothing here is live. It is the only place your 5h and 7d usage is
|
|
422
|
+
: "Not installed, so nothing here is live. It is the only place your 5h and 7d usage is published, and it is what proves a session is still open. Run: npx savemytokens install", context));
|
|
423
423
|
out.push(...helpSection("the numbers", context));
|
|
424
424
|
out.push(...helpProse("5h and 7d are published figures, not ours. Share is measured from the tokens in your transcripts. Used of it is their number split by that share, so the split between rows is ours, not theirs.", context));
|
|
425
425
|
const drift = control.unattributed ?? 0;
|
package/dist/runtime/kernel.mjs
CHANGED
|
@@ -684,21 +684,30 @@ export function usageInWindow(record, from, to) {
|
|
|
684
684
|
return { ...total, tokens, weighted };
|
|
685
685
|
}
|
|
686
686
|
|
|
687
|
+
const isPinned = (entry) => typeof entry.share === "number" && entry.share >= 0;
|
|
688
|
+
const isRunning = (entry) => entry.state === "active" || entry.state === "needs-more";
|
|
689
|
+
const holdsAClaim = (entry) => isRunning(entry) || isPinned(entry) || (entry.consumed || 0) > 0;
|
|
690
|
+
|
|
687
691
|
export function allocate(entries) {
|
|
688
692
|
const targets = new Map();
|
|
689
693
|
const eligible = [];
|
|
690
694
|
let reserved = 0;
|
|
691
695
|
let released = 0;
|
|
692
696
|
|
|
697
|
+
const claiming = entries.filter(holdsAClaim);
|
|
698
|
+
const pinnedClaims = claiming.reduce((sum, entry) => (isPinned(entry) ? sum + entry.share : sum), 0);
|
|
699
|
+
const openClaims = claiming.filter((entry) => !isPinned(entry)).length;
|
|
700
|
+
const evenClaim = openClaims > 0 ? Math.max(0, 1 - Math.min(1, pinnedClaims)) / openClaims : 0;
|
|
701
|
+
|
|
693
702
|
for (const entry of entries) {
|
|
694
|
-
|
|
695
|
-
if (state === "active" || state === "needs-more") {
|
|
703
|
+
if (isRunning(entry)) {
|
|
696
704
|
eligible.push(entry);
|
|
697
705
|
continue;
|
|
698
706
|
}
|
|
699
707
|
const keep = Math.max(0, Math.min(1, entry.consumed || 0));
|
|
708
|
+
const nominal = isPinned(entry) ? entry.share : holdsAClaim(entry) ? evenClaim : 0;
|
|
700
709
|
reserved += keep;
|
|
701
|
-
if (
|
|
710
|
+
if (nominal > keep) released += nominal - keep;
|
|
702
711
|
targets.set(entry.id, { claimantId: entry.id, target: keep, pinned: false, pool: 0, released: true });
|
|
703
712
|
}
|
|
704
713
|
|
|
@@ -712,8 +721,8 @@ export function allocate(entries) {
|
|
|
712
721
|
let budget = Math.max(0, 1 - Math.min(1, reserved));
|
|
713
722
|
if (eligible.length === 0) return { targets, unusedPool: budget };
|
|
714
723
|
|
|
715
|
-
const pinned = eligible.filter(
|
|
716
|
-
const free = eligible.filter((entry) => !(
|
|
724
|
+
const pinned = eligible.filter(isPinned);
|
|
725
|
+
const free = eligible.filter((entry) => !isPinned(entry));
|
|
717
726
|
const pinnedSum = pinned.reduce((sum, entry) => sum + entry.share, 0);
|
|
718
727
|
const scale = pinnedSum > budget && pinnedSum > 0 ? budget / pinnedSum : 1;
|
|
719
728
|
|
|
@@ -941,15 +950,16 @@ export function viewFor(plan, id) {
|
|
|
941
950
|
return plan.claimants.find((view) => view.claimant.id === id) ?? null;
|
|
942
951
|
}
|
|
943
952
|
|
|
944
|
-
const MIN_TARGET = 0.001;
|
|
945
953
|
const MAX_PRESSURE = 9.99;
|
|
946
954
|
|
|
947
955
|
export function pressureFor(consumedShare, target, quotaUsedPercent) {
|
|
948
|
-
|
|
956
|
+
const spent = consumedShare > 0 ? consumedShare : 0;
|
|
957
|
+
if (!(target > 0)) return { value: spent > 0 ? MAX_PRESSURE : 0, basis: "share" };
|
|
958
|
+
const ratio = spent / target;
|
|
949
959
|
if (typeof quotaUsedPercent === "number" && quotaUsedPercent >= 0) {
|
|
950
|
-
return { value: Math.min(MAX_PRESSURE, (quotaUsedPercent / 100) *
|
|
960
|
+
return { value: Math.min(MAX_PRESSURE, (quotaUsedPercent / 100) * ratio), basis: "budget" };
|
|
951
961
|
}
|
|
952
|
-
return { value: Math.min(MAX_PRESSURE,
|
|
962
|
+
return { value: Math.min(MAX_PRESSURE, ratio), basis: "share" };
|
|
953
963
|
}
|
|
954
964
|
|
|
955
965
|
export const POLICIES = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "savemytokens",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Give every Claude Code session a target share of your Claude window, and keep Claude aware of it. Local, private, zero setup.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"build": "tsc -p tsconfig.json && node scripts/copy-runtime.mjs",
|
|
41
41
|
"dev": "npm run build && node dist/cli.js",
|
|
42
42
|
"test": "npm run build && node --test test/*.test.js",
|
|
43
|
-
"screenshot": "npm run build && COLORTERM=truecolor node scripts/screenshot.mjs && rsvg-convert -w 1280 -h 640 assets/social.svg -o assets/social.png && cp assets/screenshot.svg assets/statusline.svg assets/f-numbers.svg assets/f-share.svg assets/f-agent.svg assets/f-resting.svg assets/f-defer.svg web/ && node scripts/stamp-assets.mjs",
|
|
43
|
+
"screenshot": "npm run build && COLORTERM=truecolor node scripts/screenshot.mjs && rsvg-convert -w 1280 -h 640 assets/social.svg -o assets/social.png && cp assets/social.png web/og-image.png && cp assets/screenshot.svg assets/statusline.svg assets/f-numbers.svg assets/f-share.svg assets/f-agent.svg assets/f-resting.svg assets/f-defer.svg web/ && node scripts/stamp-assets.mjs",
|
|
44
44
|
"prepublishOnly": "npm run build && npm test",
|
|
45
45
|
"postpublish": "node scripts/release.mjs",
|
|
46
46
|
"release": "node scripts/release.mjs",
|