@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,295 +0,0 @@
1
- import { createModuleLogger } from "@tokenbuddy/logging";
2
-
3
- const logger = createModuleLogger("tb-proxyd:credit-tracker");
4
-
5
- /**
6
- * Default window after a successful purchase during which failover is
7
- * softened (see buyer-driven-fallback-design.md §17.3). 30 seconds is the
8
- * v1.1 starting point; v1.2 keeps the same value.
9
- */
10
- export const DEFAULT_FRESH_PURCHASE_WINDOW_MS = 30_000;
11
-
12
- /**
13
- * Default threshold of "still considered fresh": the current balance must
14
- * still cover this fraction of the last purchase amount for the entry to
15
- * be treated as "刚买". 0.5 means "at least half of the purchased credit is
16
- * still untouched".
17
- */
18
- export const DEFAULT_FRESH_PURCHASE_THRESHOLD = 0.5;
19
-
20
- /**
21
- * Maximum number of automatic purchases a single session may trigger per
22
- * minute. When the budget is exhausted, route-failover must `切` rather than
23
- * `重买` (see §17.5).
24
- */
25
- export const DEFAULT_PURCHASE_BUDGET_PER_MINUTE = 3;
26
-
27
- /**
28
- * `CreditTracker` 构造选项。
29
- */
30
- export interface CreditTrackerOptions {
31
- /** "新鲜购买" 判定窗口(毫秒),默认 30s */
32
- freshPurchaseWindowMs?: number;
33
- /** "新鲜购买" 剩余比例阈值,默认 0.5(剩余 ≥ 50% 才算 fresh) */
34
- freshPurchaseThreshold?: number;
35
- /** 单 session 每分钟自动购买上限,默认 3 */
36
- purchaseBudgetPerMinute?: number;
37
- /** 时间源,默认 `Date.now`;测试可注入 */
38
- now?: () => number;
39
- }
40
-
41
- interface SellerCredit {
42
- sellerId: string;
43
- lastPurchaseAt: number;
44
- lastPurchaseAmountMicros: number;
45
- currentBalanceMicros: number;
46
- leftoverCreditMicros: number;
47
- lastUpdatedAt: number;
48
- }
49
-
50
- /**
51
- * Tracks per-seller credit state for the buyer-driven-fallback design's
52
- * "balance protection" rules. The tracker is intentionally agnostic of the
53
- * actual seller HTTP layer: callers feed in `recordPurchase`,
54
- * `recordSpend`, and `transferLeftover` events as they observe them, and
55
- * the tracker answers "is this seller still inside its fresh-purchase
56
- * window?" / "may I auto-purchase again this minute?".
57
- *
58
- * The tracker is process-local: a buyer restart resets the session
59
- * counters. See §17.11.4 for the rationale.
60
- */
61
- /**
62
- * buyer-driven-fallback 设计的"余额保护"决策器。
63
- * 跟踪每个 seller 的余额、最近购买时间、累计浪费 micros,回答:
64
- * - "该 seller 是否还在 fresh-purchase window 内?"
65
- * - "本 session 是否还能自动购买?"
66
- *
67
- * 仅进程内状态(buyer 重启会重置 session 计数器;详见 §17.11.4)。
68
- */
69
- export class CreditTracker {
70
- private readonly freshPurchaseWindowMs: number;
71
- private readonly freshPurchaseThreshold: number;
72
- private readonly purchaseBudgetPerMinute: number;
73
- private readonly now: () => number;
74
-
75
- private readonly entries = new Map<string, SellerCredit>();
76
- private readonly purchaseTimestamps: number[] = [];
77
- private totalWastedMicros = 0;
78
- private wastedSinceLastDoctorRun = 0;
79
-
80
- constructor(options: CreditTrackerOptions = {}) {
81
- this.freshPurchaseWindowMs = options.freshPurchaseWindowMs ?? DEFAULT_FRESH_PURCHASE_WINDOW_MS;
82
- this.freshPurchaseThreshold = options.freshPurchaseThreshold ?? DEFAULT_FRESH_PURCHASE_THRESHOLD;
83
- this.purchaseBudgetPerMinute = options.purchaseBudgetPerMinute ?? DEFAULT_PURCHASE_BUDGET_PER_MINUTE;
84
- this.now = options.now ?? Date.now;
85
- }
86
-
87
- /**
88
- * Register a successful purchase. Updates the seller's balance, the
89
- * session-wide budget window, and the "fresh purchase" timestamps. If
90
- * the seller is unknown, an entry is created.
91
- */
92
- recordPurchase(sellerId: string, amountMicros: number, balanceMicros: number): SellerCredit {
93
- if (!Number.isFinite(amountMicros) || amountMicros <= 0) {
94
- throw new Error("recordPurchase requires a positive amountMicros");
95
- }
96
- const ts = this.now();
97
- const previous = this.entries.get(sellerId);
98
- const entry: SellerCredit = {
99
- sellerId,
100
- lastPurchaseAt: ts,
101
- lastPurchaseAmountMicros: amountMicros,
102
- currentBalanceMicros: Math.max(0, balanceMicros),
103
- leftoverCreditMicros: previous?.leftoverCreditMicros ?? 0,
104
- lastUpdatedAt: ts
105
- };
106
- this.entries.set(sellerId, entry);
107
- this.purchaseTimestamps.push(ts);
108
- this.prunePurchaseTimestamps(ts);
109
- logger.info("credit.purchase.recorded", "seller credit purchase recorded", {
110
- sellerId,
111
- amountMicros,
112
- balanceMicros: entry.currentBalanceMicros,
113
- purchasesInLastMinute: this.purchaseTimestamps.length
114
- });
115
- return entry;
116
- }
117
-
118
- /**
119
- * Update the seller's current balance after a successful inference. The
120
- * amount spent is implicit (`previous - next`) so the caller does not
121
- * have to track it. A non-positive `balanceMicros` is treated as 0.
122
- */
123
- recordSpend(sellerId: string, balanceMicros: number): SellerCredit | undefined {
124
- const previous = this.entries.get(sellerId);
125
- if (!previous) {
126
- return undefined;
127
- }
128
- const next: SellerCredit = {
129
- ...previous,
130
- currentBalanceMicros: Math.max(0, balanceMicros),
131
- lastUpdatedAt: this.now()
132
- };
133
- this.entries.set(sellerId, next);
134
- return next;
135
- }
136
-
137
- /**
138
- * Move the seller's remaining balance to the `leftoverCreditMicros`
139
- * bucket. Called when failover triggers while a balance is still
140
- * available. The pool's pre-warm / probe logic can later attempt to
141
- * consume the leftover credit (see PR-3.1). The wasted amount is also
142
- * added to the session-level counters.
143
- */
144
- transferLeftoverToWasted(sellerId: string, reason: string): number {
145
- const previous = this.entries.get(sellerId);
146
- if (!previous) {
147
- return 0;
148
- }
149
- const wasted = previous.currentBalanceMicros;
150
- if (wasted <= 0) {
151
- return 0;
152
- }
153
- const next: SellerCredit = {
154
- ...previous,
155
- currentBalanceMicros: 0,
156
- leftoverCreditMicros: previous.leftoverCreditMicros + wasted,
157
- lastUpdatedAt: this.now()
158
- };
159
- this.entries.set(sellerId, next);
160
- this.totalWastedMicros += wasted;
161
- this.wastedSinceLastDoctorRun += wasted;
162
- logger.warn("credit.leftover.wasted", "leftover credit logged as wasted on failover", {
163
- sellerId,
164
- wastedMicros: wasted,
165
- reason,
166
- totalWastedMicros: this.totalWastedMicros
167
- });
168
- return wasted;
169
- }
170
-
171
- /**
172
- * Consume leftover credit when the seller recovers and a probe
173
- * successfully burns it down. Returns the amount actually consumed.
174
- */
175
- consumeLeftover(sellerId: string, amountMicros: number): number {
176
- const previous = this.entries.get(sellerId);
177
- if (!previous || previous.leftoverCreditMicros <= 0) {
178
- return 0;
179
- }
180
- const consume = Math.max(0, Math.min(amountMicros, previous.leftoverCreditMicros));
181
- if (consume <= 0) {
182
- return 0;
183
- }
184
- const next: SellerCredit = {
185
- ...previous,
186
- leftoverCreditMicros: previous.leftoverCreditMicros - consume,
187
- lastUpdatedAt: this.now()
188
- };
189
- this.entries.set(sellerId, next);
190
- logger.info("credit.leftover.consumed", "leftover credit consumed by recovery probe", {
191
- sellerId,
192
- consumedMicros: consume,
193
- remainingMicros: next.leftoverCreditMicros
194
- });
195
- return consume;
196
- }
197
-
198
- /**
199
- * Returns `true` when the seller is still inside the fresh-purchase
200
- * window: at least `freshPurchaseThreshold` of the most recent
201
- * purchase is still unused, and the window has not elapsed. Sellers
202
- * with no recorded purchase are never "fresh".
203
- */
204
- isInFreshPurchaseWindow(sellerId: string, now: number = this.now()): boolean {
205
- const entry = this.entries.get(sellerId);
206
- if (!entry || entry.lastPurchaseAmountMicros <= 0) {
207
- return false;
208
- }
209
- if (now - entry.lastPurchaseAt > this.freshPurchaseWindowMs) {
210
- return false;
211
- }
212
- const ratio = entry.currentBalanceMicros / entry.lastPurchaseAmountMicros;
213
- return ratio >= this.freshPurchaseThreshold;
214
- }
215
-
216
- /**
217
- * Returns `true` when the per-minute auto-purchase budget is still
218
- * available. Used by `route-failover` to refuse auto-repurchase and
219
- * force a clean `切` once the session is at risk of over-spending.
220
- */
221
- canAutoPurchase(now: number = this.now()): boolean {
222
- this.prunePurchaseTimestamps(now);
223
- return this.purchaseTimestamps.length < this.purchaseBudgetPerMinute;
224
- }
225
-
226
- /**
227
- * Snapshot for `tb doctor`. Mirrors the design's "Credit Usage" block.
228
- */
229
- summary(): CreditSummary {
230
- return {
231
- totalWastedMicros: this.totalWastedMicros,
232
- wastedSinceLastDoctorRun: this.wastedSinceLastDoctorRun,
233
- purchasesInLastMinute: this.purchaseTimestamps.length,
234
- purchaseBudgetPerMinute: this.purchaseBudgetPerMinute,
235
- freshPurchaseWindowMs: this.freshPurchaseWindowMs,
236
- freshPurchaseThreshold: this.freshPurchaseThreshold,
237
- perSeller: Array.from(this.entries.values()).map((entry) => ({
238
- sellerId: entry.sellerId,
239
- currentBalanceMicros: entry.currentBalanceMicros,
240
- lastPurchaseAmountMicros: entry.lastPurchaseAmountMicros,
241
- lastPurchaseAt: entry.lastPurchaseAt,
242
- leftoverCreditMicros: entry.leftoverCreditMicros
243
- }))
244
- };
245
- }
246
-
247
- getEntry(sellerId: string): SellerCredit | undefined {
248
- return this.entries.get(sellerId);
249
- }
250
-
251
- resetDoctorRunCounter(): void {
252
- this.wastedSinceLastDoctorRun = 0;
253
- }
254
-
255
- clear(): void {
256
- this.entries.clear();
257
- this.purchaseTimestamps.length = 0;
258
- this.totalWastedMicros = 0;
259
- this.wastedSinceLastDoctorRun = 0;
260
- }
261
-
262
- private prunePurchaseTimestamps(now: number): void {
263
- const cutoff = now - 60_000;
264
- while (this.purchaseTimestamps.length > 0 && this.purchaseTimestamps[0] < cutoff) {
265
- this.purchaseTimestamps.shift();
266
- }
267
- }
268
- }
269
-
270
- /**
271
- * `CreditTracker.summary()` 的返回结构,对应 design doc 的 "Credit Usage" 块。
272
- * `tb doctor` 直接消费此结构展示给用户。
273
- */
274
- export interface CreditSummary {
275
- /** 自 tracker 创建以来累计浪费的 micros(含 failover 触发时的余额残留) */
276
- totalWastedMicros: number;
277
- /** 上次 `resetDoctorRunCounter()` 之后又新产生的浪费 micros(doctor 显示 delta) */
278
- wastedSinceLastDoctorRun: number;
279
- /** 过去 60 秒内自动购买次数 */
280
- purchasesInLastMinute: number;
281
- /** 每分钟自动购买上限 */
282
- purchaseBudgetPerMinute: number;
283
- /** 当前 fresh-purchase 窗口长度(毫秒) */
284
- freshPurchaseWindowMs: number;
285
- /** 当前 fresh-purchase 比例阈值 */
286
- freshPurchaseThreshold: number;
287
- /** 每个 seller 的余额快照 */
288
- perSeller: Array<{
289
- sellerId: string;
290
- currentBalanceMicros: number;
291
- lastPurchaseAmountMicros: number;
292
- lastPurchaseAt: number;
293
- leftoverCreditMicros: number;
294
- }>;
295
- }