@tokenbuddy/tokenbuddy 1.0.13 → 1.0.15

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 (70) hide show
  1. package/dist/src/buyer-store.d.ts +23 -0
  2. package/dist/src/buyer-store.d.ts.map +1 -1
  3. package/dist/src/buyer-store.js +31 -6
  4. package/dist/src/buyer-store.js.map +1 -1
  5. package/dist/src/clawtip-bootstrap.d.ts +23 -0
  6. package/dist/src/clawtip-bootstrap.d.ts.map +1 -0
  7. package/dist/src/clawtip-bootstrap.js +47 -0
  8. package/dist/src/clawtip-bootstrap.js.map +1 -0
  9. package/dist/src/cli.d.ts +24 -33
  10. package/dist/src/cli.d.ts.map +1 -1
  11. package/dist/src/cli.js +157 -58
  12. package/dist/src/cli.js.map +1 -1
  13. package/dist/src/daemon.d.ts +79 -1
  14. package/dist/src/daemon.d.ts.map +1 -1
  15. package/dist/src/daemon.js +984 -23
  16. package/dist/src/daemon.js.map +1 -1
  17. package/dist/src/model-index.d.ts +1 -1
  18. package/dist/src/model-index.d.ts.map +1 -1
  19. package/dist/src/model-index.js +4 -0
  20. package/dist/src/model-index.js.map +1 -1
  21. package/dist/src/prewarm-cache.d.ts +4 -0
  22. package/dist/src/prewarm-cache.d.ts.map +1 -1
  23. package/dist/src/prewarm-cache.js +2 -1
  24. package/dist/src/prewarm-cache.js.map +1 -1
  25. package/dist/src/prewarm-scheduler.d.ts +2 -0
  26. package/dist/src/prewarm-scheduler.d.ts.map +1 -1
  27. package/dist/src/prewarm-scheduler.js +4 -2
  28. package/dist/src/prewarm-scheduler.js.map +1 -1
  29. package/dist/src/route-failover.d.ts.map +1 -1
  30. package/dist/src/route-failover.js +10 -0
  31. package/dist/src/route-failover.js.map +1 -1
  32. package/dist/src/seller-catalog.d.ts +17 -0
  33. package/dist/src/seller-catalog.d.ts.map +1 -1
  34. package/dist/src/seller-catalog.js +15 -1
  35. package/dist/src/seller-catalog.js.map +1 -1
  36. package/dist/src/seller-pool.d.ts +12 -1
  37. package/dist/src/seller-pool.d.ts.map +1 -1
  38. package/dist/src/seller-pool.js +61 -7
  39. package/dist/src/seller-pool.js.map +1 -1
  40. package/dist/src/seller-route-planner.d.ts +11 -1
  41. package/dist/src/seller-route-planner.d.ts.map +1 -1
  42. package/dist/src/seller-route-planner.js +21 -9
  43. package/dist/src/seller-route-planner.js.map +1 -1
  44. package/dist/src/seller-routing-config.d.ts +2 -0
  45. package/dist/src/seller-routing-config.d.ts.map +1 -1
  46. package/dist/src/seller-routing-config.js +11 -1
  47. package/dist/src/seller-routing-config.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/buyer-store.ts +70 -7
  50. package/src/clawtip-bootstrap.ts +64 -0
  51. package/src/cli.ts +201 -76
  52. package/src/daemon.ts +1132 -25
  53. package/src/model-index.ts +4 -1
  54. package/src/prewarm-cache.ts +6 -1
  55. package/src/prewarm-scheduler.ts +6 -2
  56. package/src/route-failover.ts +11 -0
  57. package/src/seller-catalog.ts +24 -1
  58. package/src/seller-pool.ts +69 -7
  59. package/src/seller-route-planner.ts +33 -11
  60. package/src/seller-routing-config.ts +14 -1
  61. package/static/clawtip/recharge.png +0 -0
  62. package/tests/control-plane-ui-endpoints.test.ts +559 -0
  63. package/tests/daemon-classify.test.ts +9 -0
  64. package/tests/model-index.test.ts +14 -0
  65. package/tests/route-failover.test.ts +16 -0
  66. package/tests/seller-catalog-utilities.test.ts +54 -0
  67. package/tests/seller-pool.test.ts +56 -0
  68. package/tests/seller-route-planner.test.ts +40 -0
  69. package/tests/seller-routing-config.test.ts +13 -0
  70. package/tests/tokenbuddy.test.ts +200 -7
@@ -1,3 +1,4 @@
1
+ import { isBuyerVisibleRegistrySeller } from "./seller-catalog.js";
1
2
  import { planSellerRoutes } from "./seller-routing-strategy.js";
2
3
  /**
3
4
  * 给定 (model, protocol, payment) + 三类数据源,规划出有序的 seller 路由列表。
@@ -13,7 +14,7 @@ import { planSellerRoutes } from "./seller-routing-strategy.js";
13
14
  */
14
15
  export function planSellerRouteSet(input) {
15
16
  const indexed = indexRegistrySellers(input.registrySellers);
16
- const metrics = indexMetrics(input.sellerMetrics);
17
+ const metrics = indexMetrics(input.sellerMetrics, input.now ?? Date.now());
17
18
  const source = chooseCandidateSource(input, indexed, metrics);
18
19
  const strategyPlan = planSellerRoutes(source.candidates, input.routing);
19
20
  const routes = strategyPlan.routes.map((candidate) => {
@@ -27,6 +28,8 @@ export function planSellerRouteSet(input) {
27
28
  metrics: {
28
29
  healthScore: candidate.healthScore,
29
30
  avgLatencyMs: candidate.avgLatencyMs,
31
+ ttftMs: candidate.ttftMs,
32
+ avgInferenceMs: candidate.avgInferenceMs,
30
33
  discountRatio: candidate.discountRatio,
31
34
  registryOrder: candidate.registryOrder
32
35
  }
@@ -51,7 +54,7 @@ function chooseCandidateSource(input, indexed, metrics) {
51
54
  if (!indexedSeller) {
52
55
  return undefined;
53
56
  }
54
- if (metrics.openSellerIds.has(indexedSeller.seller.id)) {
57
+ if (metrics.blockedSellerIds.has(indexedSeller.seller.id)) {
55
58
  return undefined;
56
59
  }
57
60
  return buildCandidate({
@@ -77,7 +80,7 @@ function chooseCandidateSource(input, indexed, metrics) {
77
80
  source: "registry_fallback",
78
81
  sourceReason: prewarm.length > 0 ? "prewarm_no_compatible_candidates" : "prewarm_missing",
79
82
  candidates: indexed.ordered
80
- .filter((entry) => !metrics.openSellerIds.has(entry.seller.id))
83
+ .filter((entry) => !metrics.blockedSellerIds.has(entry.seller.id))
81
84
  .map((entry) => buildCandidate({
82
85
  seller: entry.seller,
83
86
  registryOrder: entry.registryOrder,
@@ -98,6 +101,8 @@ function buildCandidate(input) {
98
101
  supportsPayment: sellerSupportsPayment(input.seller, input.paymentMethod),
99
102
  healthScore: input.metric?.healthScore,
100
103
  avgLatencyMs: input.metric?.avgLatencyMs,
104
+ ttftMs: input.metric?.ttftMs,
105
+ avgInferenceMs: input.metric?.avgInferenceMs,
101
106
  discountRatio: input.metric?.discountRatio,
102
107
  registryOrder: input.registryOrder
103
108
  };
@@ -108,21 +113,22 @@ function isSelectableCandidate(candidate) {
108
113
  function indexRegistrySellers(sellers) {
109
114
  const ordered = sellers
110
115
  .filter((seller) => Boolean(seller?.id && seller.url))
116
+ .filter((seller) => isBuyerVisibleRegistrySeller(seller))
111
117
  .map((seller, registryOrder) => ({ seller, registryOrder }));
112
118
  return {
113
119
  ordered,
114
120
  bySellerId: new Map(ordered.map((entry) => [entry.seller.id, entry]))
115
121
  };
116
122
  }
117
- function indexMetrics(metrics) {
118
- const openSellerIds = new Set((metrics ?? [])
119
- .filter((metric) => metric.circuit === "open")
123
+ function indexMetrics(metrics, now) {
124
+ const blockedSellerIds = new Set((metrics ?? [])
125
+ .filter((metric) => metric.circuit === "open" || isCapacityBlocked(metric, now))
120
126
  .map((metric) => metric.sellerId));
121
127
  return {
122
128
  bySellerId: new Map((metrics ?? [])
123
- .filter((metric) => metric.circuit !== "open")
129
+ .filter((metric) => !blockedSellerIds.has(metric.sellerId))
124
130
  .map((metric) => [metric.sellerId, metric])),
125
- openSellerIds
131
+ blockedSellerIds
126
132
  };
127
133
  }
128
134
  function mergeMetric(metric, prewarm) {
@@ -130,10 +136,16 @@ function mergeMetric(metric, prewarm) {
130
136
  sellerId: prewarm.sellerId,
131
137
  healthScore: prewarm.healthScore ?? metric?.healthScore,
132
138
  avgLatencyMs: prewarm.avgLatencyMs ?? metric?.avgLatencyMs,
139
+ ttftMs: metric?.ttftMs,
140
+ avgInferenceMs: metric?.avgInferenceMs,
133
141
  discountRatio: metric?.discountRatio,
134
- circuit: metric?.circuit
142
+ circuit: metric?.circuit,
143
+ capacityBlockedUntil: metric?.capacityBlockedUntil
135
144
  };
136
145
  }
146
+ function isCapacityBlocked(metric, now) {
147
+ return Number.isFinite(metric.capacityBlockedUntil) && metric.capacityBlockedUntil > now;
148
+ }
137
149
  function sellerSupportsModel(seller, modelId) {
138
150
  const normalized = normalizeLookupValue(modelId);
139
151
  return (seller.models ?? []).some((model) => normalizeLookupValue(model) === normalized);
@@ -1 +1 @@
1
- {"version":3,"file":"seller-route-planner.js","sourceRoot":"","sources":["../../src/seller-route-planner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"seller-route-planner.js","sourceRoot":"","sources":["../../src/seller-route-planner.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,8 @@ export interface BuyerSellerRoutingConfig extends SellerRoutingStrategyConfig {
12
12
  mode: SellerRoutingMode;
13
13
  /** 评分器:`speed` / `discount` / `balanced` */
14
14
  scorer: SellerRoutingScorer;
15
+ /** fixed 模式下按模型固定 seller;缺省时回退到全局 sellerId。 */
16
+ fixedByModel?: Record<string, string>;
15
17
  }
16
18
  /**
17
19
  * 返回 buyer 默认的 seller 路由配置:`fullAuto` + `balanced`。
@@ -1 +1 @@
1
- {"version":3,"file":"seller-routing-config.d.ts","sourceRoot":"","sources":["../../src/seller-routing-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,mBAAmB,EACnB,2BAA2B,EAC5B,MAAM,8BAA8B,CAAC;AAEtC,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAC5C,kCAAkC;AAClC,eAAO,MAAM,sBAAsB,EAAE,mBAAgC,CAAC;AAEtE;;;GAGG;AACH,MAAM,WAAW,wBAAyB,SAAQ,2BAA2B;IAC3E,6CAA6C;IAC7C,IAAI,EAAE,iBAAiB,CAAC;IACxB,4CAA4C;IAC5C,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,IAAI,wBAAwB,CAKrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,CAyBrF;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC,GAAG,SAAS,CA0BzH;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,OAAO,EACf,QAAQ,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,GAC3C,wBAAwB,CAS1B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAKzD;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI,CAOhF"}
1
+ {"version":3,"file":"seller-routing-config.d.ts","sourceRoot":"","sources":["../../src/seller-routing-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,mBAAmB,EACnB,2BAA2B,EAC5B,MAAM,8BAA8B,CAAC;AAEtC,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAC5C,kCAAkC;AAClC,eAAO,MAAM,sBAAsB,EAAE,mBAAgC,CAAC;AAEtE;;;GAGG;AACH,MAAM,WAAW,wBAAyB,SAAQ,2BAA2B;IAC3E,6CAA6C;IAC7C,IAAI,EAAE,iBAAiB,CAAC;IACxB,4CAA4C;IAC5C,MAAM,EAAE,mBAAmB,CAAC;IAC5B,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,IAAI,wBAAwB,CAKrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,CA0BrF;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC,GAAG,SAAS,CA0BzH;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,OAAO,EACf,QAAQ,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,GAC3C,wBAAwB,CAS1B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAKzD;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI,CAOhF"}
@@ -33,6 +33,7 @@ export function normalizeSellerRoutingConfig(value) {
33
33
  return {
34
34
  mode,
35
35
  sellerId: readOptionalString(value.sellerId),
36
+ fixedByModel: readFixedByModel(value.fixedByModel),
36
37
  scorer
37
38
  };
38
39
  }
@@ -121,7 +122,7 @@ export function parseSellerIdList(value) {
121
122
  * @throws 当 `fixedSet` 模式缺少 `sellerIds` 时
122
123
  */
123
124
  export function assertSellerRoutingConfig(config) {
124
- if (config.mode === "fixed" && !config.sellerId?.trim()) {
125
+ if (config.mode === "fixed" && !config.sellerId?.trim() && Object.keys(config.fixedByModel ?? {}).length === 0) {
125
126
  throw new Error("fixed routing requires --seller <sellerId>");
126
127
  }
127
128
  if (config.mode === "fixedSet" && (!config.sellerIds || config.sellerIds.length === 0)) {
@@ -158,6 +159,15 @@ function readSellerIds(value) {
158
159
  }
159
160
  return [];
160
161
  }
162
+ function readFixedByModel(value) {
163
+ if (!isObject(value)) {
164
+ return undefined;
165
+ }
166
+ const entries = Object.entries(value)
167
+ .map(([modelId, sellerId]) => [modelId.trim(), typeof sellerId === "string" ? sellerId.trim() : ""])
168
+ .filter(([modelId, sellerId]) => modelId.length > 0 && sellerId.length > 0);
169
+ return entries.length > 0 ? Object.fromEntries(entries) : undefined;
170
+ }
161
171
  function isObject(value) {
162
172
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
163
173
  }
@@ -1 +1 @@
1
- {"version":3,"file":"seller-routing-config.js","sourceRoot":"","sources":["../../src/seller-routing-config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"seller-routing-config.js","sourceRoot":"","sources":["../../src/seller-routing-config.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenbuddy/tokenbuddy",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "TokenBuddy Client CLI and Daemon",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -88,6 +88,15 @@ export interface SafePurchaseLedgerEntry {
88
88
  /**
89
89
  * buyer 端单次推理请求的账本输入。
90
90
  * `prompt` / `response` 在落库前会 hash,原始内容永不落盘。
91
+ *
92
+ * v1.2 tb-ui v1 (Iter 4):补 7 字段 — 排查 / 性能 / 路由决策溯源必备。
93
+ * - `ttftMs`:首字延迟(从请求发起到首 byte),`forwardProxyRequest` 计算
94
+ * - `fallbackCount`:本请求实际走过的 seller 数(> 1 即发生 failover)
95
+ * - `routeReason`:`planSellerRouteSet().reason`(如 `fullAuto:balanced:routes_3`)
96
+ * - `falloverChain`:failover 路径上 seller id 数组(空 = 主路径成功)
97
+ * - `upstreamStatus`:seller 透传的上游健康状态(`healthy|degraded|unhealthy|unknown`)
98
+ * - `durationMs`:总耗时(req start → response end 或失败)
99
+ * - `paymentMethod`:本请求使用的支付方式(`mock|clawtip`)
91
100
  */
92
101
  export interface InferenceLedgerInput {
93
102
  requestId: string;
@@ -106,6 +115,13 @@ export interface InferenceLedgerInput {
106
115
  balanceSource?: string;
107
116
  prompt?: string;
108
117
  response?: string;
118
+ ttftMs?: number;
119
+ fallbackCount?: number;
120
+ routeReason?: string;
121
+ falloverChain?: string[];
122
+ upstreamStatus?: string;
123
+ durationMs?: number;
124
+ paymentMethod?: string;
109
125
  }
110
126
 
111
127
  /**
@@ -130,6 +146,14 @@ export interface SafeInferenceLedgerEntry {
130
146
  promptHash?: string;
131
147
  responseHash?: string;
132
148
  createdAt: string;
149
+ // v1.2 tb-ui v1 (Iter 4):新增 7 字段
150
+ ttftMs?: number;
151
+ fallbackCount?: number;
152
+ routeReason?: string;
153
+ falloverChain?: string[];
154
+ upstreamStatus?: string;
155
+ durationMs?: number;
156
+ paymentMethod?: string;
133
157
  }
134
158
 
135
159
  /**
@@ -640,8 +664,10 @@ export class BuyerStore {
640
664
  request_id, seller_key, model_id, endpoint, status, prompt_tokens,
641
665
  completion_tokens, billed_micros, estimated_micros, settled_micros,
642
666
  settled_usd_micros, price_version, balance_snapshot_micros, balance_source,
643
- prompt_hash, response_hash, created_at
644
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
667
+ prompt_hash, response_hash, created_at,
668
+ ttft_ms, fallback_count, route_reason, fallover_chain_json, upstream_status,
669
+ duration_ms, payment_method
670
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
645
671
  ).run(
646
672
  input.requestId,
647
673
  input.sellerKey,
@@ -659,7 +685,14 @@ export class BuyerStore {
659
685
  input.balanceSource || "unknown",
660
686
  safeHash(input.prompt) || null,
661
687
  safeHash(input.response) || null,
662
- nowIso()
688
+ nowIso(),
689
+ input.ttftMs ?? null,
690
+ input.fallbackCount ?? null,
691
+ input.routeReason ?? null,
692
+ input.falloverChain ? JSON.stringify(input.falloverChain) : null,
693
+ input.upstreamStatus ?? null,
694
+ input.durationMs ?? null,
695
+ input.paymentMethod ?? null
663
696
  );
664
697
  }
665
698
 
@@ -668,7 +701,9 @@ export class BuyerStore {
668
701
  `SELECT request_id, seller_key, model_id, endpoint, status, prompt_tokens,
669
702
  completion_tokens, billed_micros, estimated_micros, settled_micros,
670
703
  settled_usd_micros, price_version, balance_snapshot_micros, balance_source,
671
- prompt_hash, response_hash, created_at
704
+ prompt_hash, response_hash, created_at,
705
+ ttft_ms, fallback_count, route_reason, fallover_chain_json, upstream_status,
706
+ duration_ms, payment_method
672
707
  FROM inference_ledger
673
708
  ORDER BY id ASC`
674
709
  ).all() as Array<{
@@ -689,6 +724,13 @@ export class BuyerStore {
689
724
  prompt_hash: string | null;
690
725
  response_hash: string | null;
691
726
  created_at: string;
727
+ ttft_ms: number | null;
728
+ fallback_count: number | null;
729
+ route_reason: string | null;
730
+ fallover_chain_json: string | null;
731
+ upstream_status: string | null;
732
+ duration_ms: number | null;
733
+ payment_method: string | null;
692
734
  }>;
693
735
 
694
736
  return rows.map(row => ({
@@ -708,7 +750,14 @@ export class BuyerStore {
708
750
  balanceSource: row.balance_source || undefined,
709
751
  promptHash: row.prompt_hash || undefined,
710
752
  responseHash: row.response_hash || undefined,
711
- createdAt: row.created_at
753
+ createdAt: row.created_at,
754
+ ttftMs: row.ttft_ms ?? undefined,
755
+ fallbackCount: row.fallback_count ?? undefined,
756
+ routeReason: row.route_reason ?? undefined,
757
+ falloverChain: row.fallover_chain_json ? JSON.parse(row.fallover_chain_json) as string[] : undefined,
758
+ upstreamStatus: row.upstream_status ?? undefined,
759
+ durationMs: row.duration_ms ?? undefined,
760
+ paymentMethod: row.payment_method ?? undefined
712
761
  }));
713
762
  }
714
763
 
@@ -784,7 +833,14 @@ export class BuyerStore {
784
833
  balance_source TEXT,
785
834
  prompt_hash TEXT,
786
835
  response_hash TEXT,
787
- created_at TEXT NOT NULL
836
+ created_at TEXT NOT NULL,
837
+ ttft_ms INTEGER,
838
+ fallback_count INTEGER,
839
+ route_reason TEXT,
840
+ fallover_chain_json TEXT,
841
+ upstream_status TEXT,
842
+ duration_ms INTEGER,
843
+ payment_method TEXT
788
844
  );
789
845
 
790
846
  CREATE TABLE IF NOT EXISTS provider_install_state (
@@ -821,7 +877,14 @@ export class BuyerStore {
821
877
  ["settled_usd_micros", "INTEGER"],
822
878
  ["price_version", "TEXT"],
823
879
  ["balance_snapshot_micros", "INTEGER"],
824
- ["balance_source", "TEXT"]
880
+ ["balance_source", "TEXT"],
881
+ ["ttft_ms", "INTEGER"],
882
+ ["fallback_count", "INTEGER"],
883
+ ["route_reason", "TEXT"],
884
+ ["fallover_chain_json", "TEXT"],
885
+ ["upstream_status", "TEXT"],
886
+ ["duration_ms", "INTEGER"],
887
+ ["payment_method", "TEXT"]
825
888
  ]) {
826
889
  this.ensureColumn("inference_ledger", column, definition);
827
890
  }
@@ -0,0 +1,64 @@
1
+ const CLAWTIP_BOOTSTRAP_PLACEHOLDER_PAY_TO = "bootstrap-pay-to";
2
+
3
+ export interface ClawtipBootstrapResponse {
4
+ activationFeeFen?: number;
5
+ payment?: {
6
+ orderNo?: string;
7
+ amountFen?: number;
8
+ payTo?: string;
9
+ encryptedData?: string;
10
+ indicator?: string;
11
+ slug?: string;
12
+ skillId?: string;
13
+ description?: string;
14
+ resourceUrl?: string;
15
+ };
16
+ }
17
+
18
+ /**
19
+ * Fetch wallet-bootstrap's ClawTip activation payment payload.
20
+ */
21
+ export async function fetchClawtipBootstrap(bootstrapUrl: string): Promise<ClawtipBootstrapResponse> {
22
+ const response = await fetch(`${bootstrapUrl.replace(/\/+$/, "")}/payments/clawtip/bootstrap`, {
23
+ method: "POST",
24
+ headers: { "Content-Type": "application/json" },
25
+ body: JSON.stringify({ clientTag: "tb-payment-add" })
26
+ });
27
+ const body = await response.json() as ClawtipBootstrapResponse & { error?: string };
28
+ if (!response.ok) {
29
+ throw new Error(body.error || `ClawTip bootstrap failed with HTTP ${response.status}`);
30
+ }
31
+ if (!body.payment?.orderNo || !body.payment.indicator || !body.payment.resourceUrl) {
32
+ throw new Error("ClawTip bootstrap response missing payment order fields");
33
+ }
34
+ if ((body.payment.payTo || "").trim() === CLAWTIP_BOOTSTRAP_PLACEHOLDER_PAY_TO) {
35
+ throw new Error(
36
+ [
37
+ `ClawTip bootstrap service is misconfigured: payTo is still the placeholder \`${CLAWTIP_BOOTSTRAP_PLACEHOLDER_PAY_TO}\`.`,
38
+ `Bootstrap URL: ${bootstrapUrl}`,
39
+ "Configure the bootstrap service with the real ClawTip merchant pay_to before retrying `tb init`.",
40
+ ].join(" ")
41
+ );
42
+ }
43
+ body.payment.resourceUrl = normalizeClawtipBootstrapResourceUrl(bootstrapUrl, body.payment.resourceUrl);
44
+ return body;
45
+ }
46
+
47
+ /**
48
+ * Normalize bootstrap resource URLs that accidentally point at the public registry path.
49
+ */
50
+ export function normalizeClawtipBootstrapResourceUrl(bootstrapUrl: string, resourceUrl: string): string {
51
+ try {
52
+ const bootstrap = new URL(bootstrapUrl);
53
+ const resource = new URL(resourceUrl);
54
+ if (resource.origin === bootstrap.origin && resource.pathname === "/registry/sellers") {
55
+ resource.pathname = bootstrap.pathname.replace(/\/+$/, "") || "/";
56
+ resource.search = "";
57
+ resource.hash = "";
58
+ return resource.toString().replace(/\/$/, "");
59
+ }
60
+ } catch {
61
+ // Keep the server-provided value when URL parsing fails.
62
+ }
63
+ return resourceUrl;
64
+ }