@tokenbuddy/tokenbuddy 1.0.36 → 1.0.38

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 (146) hide show
  1. package/dist/src/buyer-store.d.ts +7 -2
  2. package/dist/src/buyer-store.js +46 -7
  3. package/dist/src/cli.d.ts +1 -0
  4. package/dist/src/cli.js +15 -7
  5. package/dist/src/daemon.d.ts +12 -0
  6. package/dist/src/daemon.js +791 -61
  7. package/dist/src/doctor-diagnostics.js +1 -6
  8. package/dist/src/provider-install.d.ts +2 -2
  9. package/dist/src/provider-install.js +248 -2
  10. package/dist/src/seller-catalog.d.ts +21 -0
  11. package/dist/src/seller-catalog.js +17 -0
  12. package/dist/src/seller-route-planner.d.ts +4 -1
  13. package/dist/src/seller-route-planner.js +3 -0
  14. package/dist/src/seller-routing-strategy.d.ts +3 -0
  15. package/dist/src/terminal-detect.d.ts +1 -1
  16. package/dist/src/terminal-detect.js +3 -2
  17. package/dist/src/workdir.d.ts +10 -0
  18. package/dist/src/workdir.js +26 -0
  19. package/package.json +15 -2
  20. package/static/ui/assets/index-Djfl9tw5.js +271 -0
  21. package/static/ui/assets/index-DkfztCkn.css +1 -0
  22. package/static/ui/index.html +2 -2
  23. package/dist/src/buyer-store.d.ts.map +0 -1
  24. package/dist/src/buyer-store.js.map +0 -1
  25. package/dist/src/clawtip-bootstrap.d.ts.map +0 -1
  26. package/dist/src/clawtip-bootstrap.js.map +0 -1
  27. package/dist/src/cli.d.ts.map +0 -1
  28. package/dist/src/cli.js.map +0 -1
  29. package/dist/src/credit-tracker.d.ts.map +0 -1
  30. package/dist/src/credit-tracker.js.map +0 -1
  31. package/dist/src/daemon.d.ts.map +0 -1
  32. package/dist/src/daemon.js.map +0 -1
  33. package/dist/src/doctor-clawtip-wallet.d.ts.map +0 -1
  34. package/dist/src/doctor-clawtip-wallet.js.map +0 -1
  35. package/dist/src/doctor-diagnostics.d.ts.map +0 -1
  36. package/dist/src/doctor-diagnostics.js.map +0 -1
  37. package/dist/src/index.d.ts.map +0 -1
  38. package/dist/src/index.js.map +0 -1
  39. package/dist/src/init-clawtip-activation.d.ts.map +0 -1
  40. package/dist/src/init-clawtip-activation.js.map +0 -1
  41. package/dist/src/init-payment-options.d.ts.map +0 -1
  42. package/dist/src/init-payment-options.js.map +0 -1
  43. package/dist/src/init-setup.d.ts.map +0 -1
  44. package/dist/src/init-setup.js.map +0 -1
  45. package/dist/src/model-index.d.ts.map +0 -1
  46. package/dist/src/model-index.js.map +0 -1
  47. package/dist/src/package-update.d.ts.map +0 -1
  48. package/dist/src/package-update.js.map +0 -1
  49. package/dist/src/prewarm-cache.d.ts.map +0 -1
  50. package/dist/src/prewarm-cache.js.map +0 -1
  51. package/dist/src/prewarm-scheduler.d.ts.map +0 -1
  52. package/dist/src/prewarm-scheduler.js.map +0 -1
  53. package/dist/src/provider-install.d.ts.map +0 -1
  54. package/dist/src/provider-install.js.map +0 -1
  55. package/dist/src/provider-routing-config.d.ts.map +0 -1
  56. package/dist/src/provider-routing-config.js.map +0 -1
  57. package/dist/src/registry-trust.d.ts.map +0 -1
  58. package/dist/src/registry-trust.js.map +0 -1
  59. package/dist/src/route-failover.d.ts.map +0 -1
  60. package/dist/src/route-failover.js.map +0 -1
  61. package/dist/src/seller-catalog.d.ts.map +0 -1
  62. package/dist/src/seller-catalog.js.map +0 -1
  63. package/dist/src/seller-concurrency-limiter.d.ts.map +0 -1
  64. package/dist/src/seller-concurrency-limiter.js.map +0 -1
  65. package/dist/src/seller-metadata-cache.d.ts.map +0 -1
  66. package/dist/src/seller-metadata-cache.js.map +0 -1
  67. package/dist/src/seller-pool.d.ts.map +0 -1
  68. package/dist/src/seller-pool.js.map +0 -1
  69. package/dist/src/seller-route-planner.d.ts.map +0 -1
  70. package/dist/src/seller-route-planner.js.map +0 -1
  71. package/dist/src/seller-routing-config.d.ts.map +0 -1
  72. package/dist/src/seller-routing-config.js.map +0 -1
  73. package/dist/src/seller-routing-strategy.d.ts.map +0 -1
  74. package/dist/src/seller-routing-strategy.js.map +0 -1
  75. package/dist/src/stream-failover.d.ts.map +0 -1
  76. package/dist/src/stream-failover.js.map +0 -1
  77. package/dist/src/tb-clawtip-proof.d.ts.map +0 -1
  78. package/dist/src/tb-clawtip-proof.js.map +0 -1
  79. package/dist/src/tb-proxyd.d.ts.map +0 -1
  80. package/dist/src/tb-proxyd.js.map +0 -1
  81. package/dist/src/terminal-detect.d.ts.map +0 -1
  82. package/dist/src/terminal-detect.js.map +0 -1
  83. package/dist/src/terminal-image.d.ts.map +0 -1
  84. package/dist/src/terminal-image.js.map +0 -1
  85. package/src/buyer-store.ts +0 -1090
  86. package/src/clawtip-bootstrap.ts +0 -65
  87. package/src/cli.ts +0 -2243
  88. package/src/credit-tracker.ts +0 -295
  89. package/src/daemon.ts +0 -5475
  90. package/src/doctor-clawtip-wallet.ts +0 -95
  91. package/src/doctor-diagnostics.ts +0 -1026
  92. package/src/index.ts +0 -16
  93. package/src/init-clawtip-activation.ts +0 -695
  94. package/src/init-payment-options.ts +0 -373
  95. package/src/init-setup.ts +0 -165
  96. package/src/model-index.ts +0 -278
  97. package/src/package-update.ts +0 -311
  98. package/src/prewarm-cache.ts +0 -485
  99. package/src/prewarm-scheduler.ts +0 -675
  100. package/src/provider-install.ts +0 -1006
  101. package/src/provider-routing-config.ts +0 -410
  102. package/src/registry-trust.ts +0 -51
  103. package/src/route-failover.ts +0 -304
  104. package/src/seller-catalog.ts +0 -505
  105. package/src/seller-concurrency-limiter.ts +0 -161
  106. package/src/seller-metadata-cache.ts +0 -91
  107. package/src/seller-pool.ts +0 -557
  108. package/src/seller-route-planner.ts +0 -513
  109. package/src/seller-routing-config.ts +0 -211
  110. package/src/seller-routing-strategy.ts +0 -362
  111. package/src/stream-failover.ts +0 -152
  112. package/src/tb-clawtip-proof.ts +0 -28
  113. package/src/tb-proxyd.ts +0 -101
  114. package/src/terminal-detect.ts +0 -333
  115. package/src/terminal-image.ts +0 -228
  116. package/static/ui/assets/index-0MVXD7bH.css +0 -1
  117. package/static/ui/assets/index-BVbeDEwq.js +0 -271
  118. package/static/ui/assets/index-BVbeDEwq.js.map +0 -1
  119. package/tests/cli-routing.test.ts +0 -363
  120. package/tests/control-plane-ui-endpoints.test.ts +0 -1630
  121. package/tests/credit-tracker.test.ts +0 -165
  122. package/tests/daemon-413-fallback.test.ts +0 -92
  123. package/tests/daemon-classify.test.ts +0 -452
  124. package/tests/daemon-roles.test.ts +0 -92
  125. package/tests/daemon-trusted-registry-cache.test.ts +0 -132
  126. package/tests/e2e.test.ts +0 -366
  127. package/tests/image-generation-e2e.test.ts +0 -230
  128. package/tests/model-index.test.ts +0 -198
  129. package/tests/package-update.test.ts +0 -147
  130. package/tests/prewarm-cache.test.ts +0 -296
  131. package/tests/prewarm-scheduler.test.ts +0 -367
  132. package/tests/provider-routing-config.test.ts +0 -150
  133. package/tests/registry-trust.test.ts +0 -28
  134. package/tests/route-failover.test.ts +0 -222
  135. package/tests/seller-catalog-413.test.ts +0 -120
  136. package/tests/seller-catalog-utilities.test.ts +0 -124
  137. package/tests/seller-concurrency-limiter.test.ts +0 -83
  138. package/tests/seller-metadata-cache.test.ts +0 -89
  139. package/tests/seller-pool.test.ts +0 -365
  140. package/tests/seller-route-planner.test.ts +0 -312
  141. package/tests/seller-routing-config.test.ts +0 -124
  142. package/tests/seller-routing-strategy.test.ts +0 -167
  143. package/tests/stream-failover.test.ts +0 -52
  144. package/tests/thousand-seller.test.ts +0 -151
  145. package/tests/tokenbuddy.test.ts +0 -4043
  146. 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
- });