@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,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
- }