@tokenbuddy/tokenbuddy 1.0.36 → 1.0.37

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 (143) hide show
  1. package/dist/src/buyer-store.d.ts +6 -1
  2. package/dist/src/buyer-store.js +43 -4
  3. package/dist/src/cli.js +2 -2
  4. package/dist/src/daemon.d.ts +12 -0
  5. package/dist/src/daemon.js +791 -61
  6. package/dist/src/doctor-diagnostics.js +1 -6
  7. package/dist/src/provider-install.d.ts +2 -2
  8. package/dist/src/provider-install.js +248 -2
  9. package/dist/src/seller-catalog.d.ts +21 -0
  10. package/dist/src/seller-catalog.js +17 -0
  11. package/dist/src/seller-route-planner.d.ts +4 -1
  12. package/dist/src/seller-route-planner.js +3 -0
  13. package/dist/src/seller-routing-strategy.d.ts +3 -0
  14. package/dist/src/terminal-detect.d.ts +1 -1
  15. package/dist/src/terminal-detect.js +3 -2
  16. package/package.json +15 -2
  17. package/static/ui/assets/index-Djfl9tw5.js +271 -0
  18. package/static/ui/assets/index-DkfztCkn.css +1 -0
  19. package/static/ui/index.html +2 -2
  20. package/dist/src/buyer-store.d.ts.map +0 -1
  21. package/dist/src/buyer-store.js.map +0 -1
  22. package/dist/src/clawtip-bootstrap.d.ts.map +0 -1
  23. package/dist/src/clawtip-bootstrap.js.map +0 -1
  24. package/dist/src/cli.d.ts.map +0 -1
  25. package/dist/src/cli.js.map +0 -1
  26. package/dist/src/credit-tracker.d.ts.map +0 -1
  27. package/dist/src/credit-tracker.js.map +0 -1
  28. package/dist/src/daemon.d.ts.map +0 -1
  29. package/dist/src/daemon.js.map +0 -1
  30. package/dist/src/doctor-clawtip-wallet.d.ts.map +0 -1
  31. package/dist/src/doctor-clawtip-wallet.js.map +0 -1
  32. package/dist/src/doctor-diagnostics.d.ts.map +0 -1
  33. package/dist/src/doctor-diagnostics.js.map +0 -1
  34. package/dist/src/index.d.ts.map +0 -1
  35. package/dist/src/index.js.map +0 -1
  36. package/dist/src/init-clawtip-activation.d.ts.map +0 -1
  37. package/dist/src/init-clawtip-activation.js.map +0 -1
  38. package/dist/src/init-payment-options.d.ts.map +0 -1
  39. package/dist/src/init-payment-options.js.map +0 -1
  40. package/dist/src/init-setup.d.ts.map +0 -1
  41. package/dist/src/init-setup.js.map +0 -1
  42. package/dist/src/model-index.d.ts.map +0 -1
  43. package/dist/src/model-index.js.map +0 -1
  44. package/dist/src/package-update.d.ts.map +0 -1
  45. package/dist/src/package-update.js.map +0 -1
  46. package/dist/src/prewarm-cache.d.ts.map +0 -1
  47. package/dist/src/prewarm-cache.js.map +0 -1
  48. package/dist/src/prewarm-scheduler.d.ts.map +0 -1
  49. package/dist/src/prewarm-scheduler.js.map +0 -1
  50. package/dist/src/provider-install.d.ts.map +0 -1
  51. package/dist/src/provider-install.js.map +0 -1
  52. package/dist/src/provider-routing-config.d.ts.map +0 -1
  53. package/dist/src/provider-routing-config.js.map +0 -1
  54. package/dist/src/registry-trust.d.ts.map +0 -1
  55. package/dist/src/registry-trust.js.map +0 -1
  56. package/dist/src/route-failover.d.ts.map +0 -1
  57. package/dist/src/route-failover.js.map +0 -1
  58. package/dist/src/seller-catalog.d.ts.map +0 -1
  59. package/dist/src/seller-catalog.js.map +0 -1
  60. package/dist/src/seller-concurrency-limiter.d.ts.map +0 -1
  61. package/dist/src/seller-concurrency-limiter.js.map +0 -1
  62. package/dist/src/seller-metadata-cache.d.ts.map +0 -1
  63. package/dist/src/seller-metadata-cache.js.map +0 -1
  64. package/dist/src/seller-pool.d.ts.map +0 -1
  65. package/dist/src/seller-pool.js.map +0 -1
  66. package/dist/src/seller-route-planner.d.ts.map +0 -1
  67. package/dist/src/seller-route-planner.js.map +0 -1
  68. package/dist/src/seller-routing-config.d.ts.map +0 -1
  69. package/dist/src/seller-routing-config.js.map +0 -1
  70. package/dist/src/seller-routing-strategy.d.ts.map +0 -1
  71. package/dist/src/seller-routing-strategy.js.map +0 -1
  72. package/dist/src/stream-failover.d.ts.map +0 -1
  73. package/dist/src/stream-failover.js.map +0 -1
  74. package/dist/src/tb-clawtip-proof.d.ts.map +0 -1
  75. package/dist/src/tb-clawtip-proof.js.map +0 -1
  76. package/dist/src/tb-proxyd.d.ts.map +0 -1
  77. package/dist/src/tb-proxyd.js.map +0 -1
  78. package/dist/src/terminal-detect.d.ts.map +0 -1
  79. package/dist/src/terminal-detect.js.map +0 -1
  80. package/dist/src/terminal-image.d.ts.map +0 -1
  81. package/dist/src/terminal-image.js.map +0 -1
  82. package/src/buyer-store.ts +0 -1090
  83. package/src/clawtip-bootstrap.ts +0 -65
  84. package/src/cli.ts +0 -2243
  85. package/src/credit-tracker.ts +0 -295
  86. package/src/daemon.ts +0 -5475
  87. package/src/doctor-clawtip-wallet.ts +0 -95
  88. package/src/doctor-diagnostics.ts +0 -1026
  89. package/src/index.ts +0 -16
  90. package/src/init-clawtip-activation.ts +0 -695
  91. package/src/init-payment-options.ts +0 -373
  92. package/src/init-setup.ts +0 -165
  93. package/src/model-index.ts +0 -278
  94. package/src/package-update.ts +0 -311
  95. package/src/prewarm-cache.ts +0 -485
  96. package/src/prewarm-scheduler.ts +0 -675
  97. package/src/provider-install.ts +0 -1006
  98. package/src/provider-routing-config.ts +0 -410
  99. package/src/registry-trust.ts +0 -51
  100. package/src/route-failover.ts +0 -304
  101. package/src/seller-catalog.ts +0 -505
  102. package/src/seller-concurrency-limiter.ts +0 -161
  103. package/src/seller-metadata-cache.ts +0 -91
  104. package/src/seller-pool.ts +0 -557
  105. package/src/seller-route-planner.ts +0 -513
  106. package/src/seller-routing-config.ts +0 -211
  107. package/src/seller-routing-strategy.ts +0 -362
  108. package/src/stream-failover.ts +0 -152
  109. package/src/tb-clawtip-proof.ts +0 -28
  110. package/src/tb-proxyd.ts +0 -101
  111. package/src/terminal-detect.ts +0 -333
  112. package/src/terminal-image.ts +0 -228
  113. package/static/ui/assets/index-0MVXD7bH.css +0 -1
  114. package/static/ui/assets/index-BVbeDEwq.js +0 -271
  115. package/static/ui/assets/index-BVbeDEwq.js.map +0 -1
  116. package/tests/cli-routing.test.ts +0 -363
  117. package/tests/control-plane-ui-endpoints.test.ts +0 -1630
  118. package/tests/credit-tracker.test.ts +0 -165
  119. package/tests/daemon-413-fallback.test.ts +0 -92
  120. package/tests/daemon-classify.test.ts +0 -452
  121. package/tests/daemon-roles.test.ts +0 -92
  122. package/tests/daemon-trusted-registry-cache.test.ts +0 -132
  123. package/tests/e2e.test.ts +0 -366
  124. package/tests/image-generation-e2e.test.ts +0 -230
  125. package/tests/model-index.test.ts +0 -198
  126. package/tests/package-update.test.ts +0 -147
  127. package/tests/prewarm-cache.test.ts +0 -296
  128. package/tests/prewarm-scheduler.test.ts +0 -367
  129. package/tests/provider-routing-config.test.ts +0 -150
  130. package/tests/registry-trust.test.ts +0 -28
  131. package/tests/route-failover.test.ts +0 -222
  132. package/tests/seller-catalog-413.test.ts +0 -120
  133. package/tests/seller-catalog-utilities.test.ts +0 -124
  134. package/tests/seller-concurrency-limiter.test.ts +0 -83
  135. package/tests/seller-metadata-cache.test.ts +0 -89
  136. package/tests/seller-pool.test.ts +0 -365
  137. package/tests/seller-route-planner.test.ts +0 -312
  138. package/tests/seller-routing-config.test.ts +0 -124
  139. package/tests/seller-routing-strategy.test.ts +0 -167
  140. package/tests/stream-failover.test.ts +0 -52
  141. package/tests/thousand-seller.test.ts +0 -151
  142. package/tests/tokenbuddy.test.ts +0 -4043
  143. package/tsconfig.json +0 -8
@@ -1,165 +0,0 @@
1
- import {
2
- CreditTracker,
3
- DEFAULT_FRESH_PURCHASE_THRESHOLD,
4
- DEFAULT_FRESH_PURCHASE_WINDOW_MS,
5
- DEFAULT_PURCHASE_BUDGET_PER_MINUTE
6
- } from "../src/credit-tracker.js";
7
-
8
- describe("CreditTracker", () => {
9
- function makeClock(start = 1_000_000): { now: number; advance: (ms: number) => void } {
10
- const clock = { now: start, advance: (ms: number) => { clock.now += ms; } };
11
- return clock;
12
- }
13
-
14
- test("recordPurchase creates an entry with the supplied balance", () => {
15
- const clock = makeClock();
16
- const tracker = new CreditTracker({ now: () => clock.now });
17
- const entry = tracker.recordPurchase("s1", 1_000_000, 1_000_000);
18
- expect(entry.sellerId).toBe("s1");
19
- expect(entry.lastPurchaseAmountMicros).toBe(1_000_000);
20
- expect(entry.currentBalanceMicros).toBe(1_000_000);
21
- expect(entry.leftoverCreditMicros).toBe(0);
22
- expect(tracker.summary().purchasesInLastMinute).toBe(1);
23
- });
24
-
25
- test("recordPurchase rejects non-positive amounts", () => {
26
- const tracker = new CreditTracker();
27
- expect(() => tracker.recordPurchase("s1", 0, 0)).toThrow();
28
- expect(() => tracker.recordPurchase("s1", -1, -1)).toThrow();
29
- });
30
-
31
- test("recordSpend clamps to zero and is a no-op for unknown sellers", () => {
32
- const tracker = new CreditTracker();
33
- expect(tracker.recordSpend("missing", 100)).toBeUndefined();
34
- tracker.recordPurchase("s1", 1_000_000, 1_000_000);
35
- const updated = tracker.recordSpend("s1", 600_000);
36
- expect(updated?.currentBalanceMicros).toBe(600_000);
37
- const zeroed = tracker.recordSpend("s1", -10);
38
- expect(zeroed?.currentBalanceMicros).toBe(0);
39
- });
40
-
41
- test("isInFreshPurchaseWindow is true only when balance ratio and time window are both satisfied", () => {
42
- const clock = makeClock();
43
- const tracker = new CreditTracker({ now: () => clock.now });
44
- tracker.recordPurchase("s1", 1_000_000, 1_000_000);
45
-
46
- // Right after purchase: 100% balance, well within the window.
47
- expect(tracker.isInFreshPurchaseWindow("s1")).toBe(true);
48
-
49
- // After 50% spend: ratio = 0.5, still at the default threshold.
50
- tracker.recordSpend("s1", 500_000);
51
- expect(tracker.isInFreshPurchaseWindow("s1")).toBe(true);
52
-
53
- // After more spend the ratio drops below threshold.
54
- tracker.recordSpend("s1", 100_000);
55
- expect(tracker.isInFreshPurchaseWindow("s1")).toBe(false);
56
-
57
- // Replenish via a new purchase and verify window restarts.
58
- clock.advance(1_000);
59
- tracker.recordPurchase("s1", 1_000_000, 1_000_000);
60
- expect(tracker.isInFreshPurchaseWindow("s1")).toBe(true);
61
-
62
- // Advance past the window: even a full balance no longer counts.
63
- clock.advance(DEFAULT_FRESH_PURCHASE_WINDOW_MS + 1);
64
- expect(tracker.isInFreshPurchaseWindow("s1")).toBe(false);
65
- });
66
-
67
- test("isInFreshPurchaseWindow returns false for sellers without any purchase", () => {
68
- const tracker = new CreditTracker();
69
- expect(tracker.isInFreshPurchaseWindow("never-purchased")).toBe(false);
70
- });
71
-
72
- test("canAutoPurchase enforces a per-minute budget and prunes old timestamps", () => {
73
- const clock = makeClock();
74
- const tracker = new CreditTracker({ now: () => clock.now });
75
-
76
- expect(tracker.canAutoPurchase()).toBe(true);
77
- tracker.recordPurchase("s1", 100, 100);
78
- tracker.recordPurchase("s2", 100, 100);
79
- tracker.recordPurchase("s3", 100, 100);
80
- expect(tracker.canAutoPurchase()).toBe(false); // budget hit
81
-
82
- // Advance past the 60s window: oldest timestamps fall out.
83
- clock.advance(60_001);
84
- expect(tracker.canAutoPurchase()).toBe(true);
85
- });
86
-
87
- test("transferLeftoverToWasted moves the current balance into the leftover bucket and tracks waste", () => {
88
- const clock = makeClock();
89
- const tracker = new CreditTracker({ now: () => clock.now });
90
- tracker.recordPurchase("s1", 1_000_000, 800_000);
91
- const wasted = tracker.transferLeftoverToWasted("s1", "failover");
92
- expect(wasted).toBe(800_000);
93
- const entry = tracker.getEntry("s1");
94
- expect(entry?.currentBalanceMicros).toBe(0);
95
- expect(entry?.leftoverCreditMicros).toBe(800_000);
96
- const summary = tracker.summary();
97
- expect(summary.totalWastedMicros).toBe(800_000);
98
- expect(summary.wastedSinceLastDoctorRun).toBe(800_000);
99
- });
100
-
101
- test("transferLeftoverToWasted is a no-op for unknown sellers or empty balances", () => {
102
- const tracker = new CreditTracker();
103
- expect(tracker.transferLeftoverToWasted("missing", "x")).toBe(0);
104
- tracker.recordPurchase("s1", 100, 0);
105
- expect(tracker.transferLeftoverToWasted("s1", "x")).toBe(0);
106
- });
107
-
108
- test("consumeLeftover drains the leftover bucket up to the requested amount", () => {
109
- const tracker = new CreditTracker();
110
- tracker.recordPurchase("s1", 1_000_000, 500_000);
111
- tracker.transferLeftoverToWasted("s1", "failover");
112
- expect(tracker.consumeLeftover("s1", 300_000)).toBe(300_000);
113
- expect(tracker.getEntry("s1")?.leftoverCreditMicros).toBe(200_000);
114
- // Requesting more than available returns only the available amount.
115
- expect(tracker.consumeLeftover("s1", 999_999)).toBe(200_000);
116
- expect(tracker.getEntry("s1")?.leftoverCreditMicros).toBe(0);
117
- });
118
-
119
- test("consumeLeftover is a no-op for sellers without leftover", () => {
120
- const tracker = new CreditTracker();
121
- expect(tracker.consumeLeftover("missing", 100)).toBe(0);
122
- });
123
-
124
- test("summary includes per-seller entries with latest values", () => {
125
- const clock = makeClock();
126
- const tracker = new CreditTracker({ now: () => clock.now });
127
- tracker.recordPurchase("s1", 1_000_000, 1_000_000);
128
- clock.advance(1_000);
129
- tracker.recordPurchase("s2", 500_000, 500_000);
130
- const summary = tracker.summary();
131
- expect(summary.perSeller).toHaveLength(2);
132
- const s2 = summary.perSeller.find((entry) => entry.sellerId === "s2");
133
- expect(s2?.currentBalanceMicros).toBe(500_000);
134
- expect(s2?.lastPurchaseAt).toBe(clock.now);
135
- });
136
-
137
- test("resetDoctorRunCounter clears the wasted-since-last-doctor counter", () => {
138
- const tracker = new CreditTracker();
139
- tracker.recordPurchase("s1", 1_000_000, 500_000);
140
- tracker.transferLeftoverToWasted("s1", "failover");
141
- expect(tracker.summary().wastedSinceLastDoctorRun).toBe(500_000);
142
- tracker.resetDoctorRunCounter();
143
- expect(tracker.summary().wastedSinceLastDoctorRun).toBe(0);
144
- // totalWastedMicros is preserved.
145
- expect(tracker.summary().totalWastedMicros).toBe(500_000);
146
- });
147
-
148
- test("clear removes all entries and resets all counters", () => {
149
- const tracker = new CreditTracker();
150
- tracker.recordPurchase("s1", 100, 100);
151
- tracker.transferLeftoverToWasted("s1", "failover");
152
- tracker.clear();
153
- const summary = tracker.summary();
154
- expect(summary.totalWastedMicros).toBe(0);
155
- expect(summary.wastedSinceLastDoctorRun).toBe(0);
156
- expect(summary.purchasesInLastMinute).toBe(0);
157
- expect(summary.perSeller).toEqual([]);
158
- });
159
-
160
- test("default constants match the v1.1 / v1.2 design", () => {
161
- expect(DEFAULT_FRESH_PURCHASE_WINDOW_MS).toBe(30_000);
162
- expect(DEFAULT_FRESH_PURCHASE_THRESHOLD).toBe(0.5);
163
- expect(DEFAULT_PURCHASE_BUDGET_PER_MINUTE).toBe(3);
164
- });
165
- });
@@ -1,92 +0,0 @@
1
- import * as http from "http";
2
- import * as fs from "fs";
3
- import * as path from "path";
4
- import { AddressInfo } from "net";
5
- import { TokenbuddyDaemon } from "../src/daemon.js";
6
-
7
- /**
8
- * v1.2 §18.9: when the bootstrap returns 413 (registry over 1MB), the
9
- * daemon must fall back to the last successfully fetched snapshot
10
- * instead of failing every request. This is the buyer-side counterpart
11
- * to the bootstrap's hard 1MB cap.
12
- */
13
- describe("TokenbuddyDaemon registry 413 stale-fallback", () => {
14
- const TEMP_DB = path.resolve(__dirname, "../../data-test/413-fallback-test.db");
15
- let bootstrapServer: http.Server;
16
- let bootstrapPort: number;
17
- let daemon: TokenbuddyDaemon;
18
- let daemonProxyPort: number;
19
-
20
- function rmDb(): void {
21
- for (const suffix of ["", "-wal", "-shm"]) {
22
- const file = TEMP_DB + suffix;
23
- if (fs.existsSync(file)) {
24
- fs.unlinkSync(file);
25
- }
26
- }
27
- }
28
-
29
- beforeAll((done) => {
30
- bootstrapServer = http.createServer((_req, res) => {
31
- res.setHeader("Content-Type", "application/json");
32
- // Always 200 for the boot fetch. Tests that want 413 do not
33
- // need to flip this server; they instead verify the catch-block
34
- // in `fetchRegistry` via the dedicated unit test in
35
- // `seller-catalog-413.test.ts`.
36
- res.statusCode = 200;
37
- res.end(JSON.stringify({
38
- version: 1,
39
- defaultSeller: "primary-seller",
40
- sellers: [
41
- {
42
- id: "primary-seller",
43
- url: "https://primary.example.com",
44
- supportedProtocols: ["chat_completions"],
45
- paymentMethods: ["mock"],
46
- models: ["gpt-4o"]
47
- }
48
- ]
49
- }));
50
- });
51
- bootstrapServer.listen(0, "127.0.0.1", () => {
52
- bootstrapPort = (bootstrapServer.address() as AddressInfo).port;
53
- done();
54
- });
55
- });
56
-
57
- afterAll((done) => {
58
- bootstrapServer.close(() => done());
59
- });
60
-
61
- beforeEach(() => {
62
- rmDb();
63
- daemon = new TokenbuddyDaemon({
64
- controlPort: 0,
65
- proxyPort: 0,
66
- dbPath: TEMP_DB,
67
- sellerRegistryUrl: `http://127.0.0.1:${bootstrapPort}/registry/sellers`
68
- });
69
- daemon.start();
70
- const proxyServer = (daemon as unknown as { proxyServer: { address(): AddressInfo } }).proxyServer;
71
- daemonProxyPort = proxyServer.address().port;
72
- });
73
-
74
- afterEach(async () => {
75
- daemon.stop();
76
- // Drain any in-flight prewarm scheduler work to avoid jest
77
- // open-handle warnings. The daemon's stop() is fire-and-forget.
78
- await new Promise<void>((resolve) => setTimeout(resolve, 50));
79
- rmDb();
80
- });
81
-
82
- test("daemon stays alive after a successful boot against the bootstrap", async () => {
83
- // The buyer must surface this as a typed error so the daemon can
84
- // fall back to the last-known snapshot. The fetch logic is covered
85
- // by `seller-catalog-413.test.ts`; here we just assert the
86
- // happy-path control plane is up.
87
- const controlPort = (daemon as unknown as { controlServer: { address(): AddressInfo } }).controlServer.address().port;
88
- const health = await (await fetch(`http://127.0.0.1:${controlPort}/health`)).json() as { status: string };
89
- expect(health.status).toBe("ok");
90
- expect(typeof daemonProxyPort).toBe("number");
91
- });
92
- });