luckerr 0.41.0

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 (156) hide show
  1. package/README.md +267 -0
  2. package/README.zh-CN.md +237 -0
  3. package/dashboard/app.css +3022 -0
  4. package/dashboard/dist/app.js +30137 -0
  5. package/dashboard/dist/app.js.map +1 -0
  6. package/dashboard/dist/vendor-hljs.css +10 -0
  7. package/dashboard/dist/vendor-uplot.css +1 -0
  8. package/dashboard/index.html +19 -0
  9. package/data/deepseek-tokenizer.json.gz +0 -0
  10. package/dist/cli/acp-EOOAI4F5.js +712 -0
  11. package/dist/cli/acp-EOOAI4F5.js.map +1 -0
  12. package/dist/cli/chat-7J6GJXL2.js +51 -0
  13. package/dist/cli/chat-7J6GJXL2.js.map +1 -0
  14. package/dist/cli/chunk-2425HK6U.js +54 -0
  15. package/dist/cli/chunk-2425HK6U.js.map +1 -0
  16. package/dist/cli/chunk-25T6CVUP.js +172 -0
  17. package/dist/cli/chunk-25T6CVUP.js.map +1 -0
  18. package/dist/cli/chunk-2UQP6H6T.js +31 -0
  19. package/dist/cli/chunk-2UQP6H6T.js.map +1 -0
  20. package/dist/cli/chunk-56OAJILV.js +47 -0
  21. package/dist/cli/chunk-56OAJILV.js.map +1 -0
  22. package/dist/cli/chunk-5FTI4KXH.js +150 -0
  23. package/dist/cli/chunk-5FTI4KXH.js.map +1 -0
  24. package/dist/cli/chunk-5TWQD73O.js +2846 -0
  25. package/dist/cli/chunk-5TWQD73O.js.map +1 -0
  26. package/dist/cli/chunk-653BOCMK.js +40 -0
  27. package/dist/cli/chunk-653BOCMK.js.map +1 -0
  28. package/dist/cli/chunk-6ALJTWWQ.js +2663 -0
  29. package/dist/cli/chunk-6ALJTWWQ.js.map +1 -0
  30. package/dist/cli/chunk-6DRKA2IL.js +341 -0
  31. package/dist/cli/chunk-6DRKA2IL.js.map +1 -0
  32. package/dist/cli/chunk-6LV63NJV.js +634 -0
  33. package/dist/cli/chunk-6LV63NJV.js.map +1 -0
  34. package/dist/cli/chunk-74EX7SUH.js +25293 -0
  35. package/dist/cli/chunk-74EX7SUH.js.map +1 -0
  36. package/dist/cli/chunk-74U5RKTX.js +60611 -0
  37. package/dist/cli/chunk-74U5RKTX.js.map +1 -0
  38. package/dist/cli/chunk-ANJSUESV.js +143 -0
  39. package/dist/cli/chunk-ANJSUESV.js.map +1 -0
  40. package/dist/cli/chunk-DB2Z3DKZ.js +54 -0
  41. package/dist/cli/chunk-DB2Z3DKZ.js.map +1 -0
  42. package/dist/cli/chunk-DDIH3ZAA.js +400 -0
  43. package/dist/cli/chunk-DDIH3ZAA.js.map +1 -0
  44. package/dist/cli/chunk-ELN3Z3B2.js +621 -0
  45. package/dist/cli/chunk-ELN3Z3B2.js.map +1 -0
  46. package/dist/cli/chunk-F6BSQJGV.js +200 -0
  47. package/dist/cli/chunk-F6BSQJGV.js.map +1 -0
  48. package/dist/cli/chunk-FET2UAG5.js +246 -0
  49. package/dist/cli/chunk-FET2UAG5.js.map +1 -0
  50. package/dist/cli/chunk-FFJ342IJ.js +190 -0
  51. package/dist/cli/chunk-FFJ342IJ.js.map +1 -0
  52. package/dist/cli/chunk-GB3247B6.js +130 -0
  53. package/dist/cli/chunk-GB3247B6.js.map +1 -0
  54. package/dist/cli/chunk-HC2J4U3G.js +373 -0
  55. package/dist/cli/chunk-HC2J4U3G.js.map +1 -0
  56. package/dist/cli/chunk-HRUZAIHQ.js +42 -0
  57. package/dist/cli/chunk-HRUZAIHQ.js.map +1 -0
  58. package/dist/cli/chunk-J3ZJFUDL.js +308 -0
  59. package/dist/cli/chunk-J3ZJFUDL.js.map +1 -0
  60. package/dist/cli/chunk-J5XJHLWM.js +55 -0
  61. package/dist/cli/chunk-J5XJHLWM.js.map +1 -0
  62. package/dist/cli/chunk-JFGLMRZ6.js +160 -0
  63. package/dist/cli/chunk-JFGLMRZ6.js.map +1 -0
  64. package/dist/cli/chunk-JMBMLOBP.js +26 -0
  65. package/dist/cli/chunk-JMBMLOBP.js.map +1 -0
  66. package/dist/cli/chunk-JMWHXZEL.js +551 -0
  67. package/dist/cli/chunk-JMWHXZEL.js.map +1 -0
  68. package/dist/cli/chunk-KEQGPJBO.js +209 -0
  69. package/dist/cli/chunk-KEQGPJBO.js.map +1 -0
  70. package/dist/cli/chunk-M4K6U37F.js +232 -0
  71. package/dist/cli/chunk-M4K6U37F.js.map +1 -0
  72. package/dist/cli/chunk-MIJI2WMN.js +95 -0
  73. package/dist/cli/chunk-MIJI2WMN.js.map +1 -0
  74. package/dist/cli/chunk-MPAO3JNR.js +128 -0
  75. package/dist/cli/chunk-MPAO3JNR.js.map +1 -0
  76. package/dist/cli/chunk-PZOFBEDC.js +873 -0
  77. package/dist/cli/chunk-PZOFBEDC.js.map +1 -0
  78. package/dist/cli/chunk-RAILYQLN.js +46 -0
  79. package/dist/cli/chunk-RAILYQLN.js.map +1 -0
  80. package/dist/cli/chunk-RR35VQVT.js +90 -0
  81. package/dist/cli/chunk-RR35VQVT.js.map +1 -0
  82. package/dist/cli/chunk-RRA7VPW4.js +417 -0
  83. package/dist/cli/chunk-RRA7VPW4.js.map +1 -0
  84. package/dist/cli/chunk-RU36QVN3.js +452 -0
  85. package/dist/cli/chunk-RU36QVN3.js.map +1 -0
  86. package/dist/cli/chunk-RUBIINXR.js +1819 -0
  87. package/dist/cli/chunk-RUBIINXR.js.map +1 -0
  88. package/dist/cli/chunk-S4XVGLRW.js +499 -0
  89. package/dist/cli/chunk-S4XVGLRW.js.map +1 -0
  90. package/dist/cli/chunk-TUK7OWJA.js +51 -0
  91. package/dist/cli/chunk-TUK7OWJA.js.map +1 -0
  92. package/dist/cli/chunk-VALDDV76.js +580 -0
  93. package/dist/cli/chunk-VALDDV76.js.map +1 -0
  94. package/dist/cli/chunk-WQOGPYGN.js +11390 -0
  95. package/dist/cli/chunk-WQOGPYGN.js.map +1 -0
  96. package/dist/cli/chunk-WREKDFXT.js +34320 -0
  97. package/dist/cli/chunk-WREKDFXT.js.map +1 -0
  98. package/dist/cli/chunk-Y7XQU2EL.js +270 -0
  99. package/dist/cli/chunk-Y7XQU2EL.js.map +1 -0
  100. package/dist/cli/chunk-YBVCZJU4.js +54 -0
  101. package/dist/cli/chunk-YBVCZJU4.js.map +1 -0
  102. package/dist/cli/chunk-YLIHDXUQ.js +749 -0
  103. package/dist/cli/chunk-YLIHDXUQ.js.map +1 -0
  104. package/dist/cli/chunk-YV5XXFD7.js +767 -0
  105. package/dist/cli/chunk-YV5XXFD7.js.map +1 -0
  106. package/dist/cli/chunk-ZRCNIYRQ.js +101 -0
  107. package/dist/cli/chunk-ZRCNIYRQ.js.map +1 -0
  108. package/dist/cli/code-CRKVCMFZ.js +155 -0
  109. package/dist/cli/code-CRKVCMFZ.js.map +1 -0
  110. package/dist/cli/commands-QLMD3T7B.js +356 -0
  111. package/dist/cli/commands-QLMD3T7B.js.map +1 -0
  112. package/dist/cli/commit-53PP32NC.js +293 -0
  113. package/dist/cli/commit-53PP32NC.js.map +1 -0
  114. package/dist/cli/desktop-R6W5CLJ5.js +1046 -0
  115. package/dist/cli/desktop-R6W5CLJ5.js.map +1 -0
  116. package/dist/cli/devtools-YECO25QO.js +3719 -0
  117. package/dist/cli/devtools-YECO25QO.js.map +1 -0
  118. package/dist/cli/diff-LYNRCJZE.js +166 -0
  119. package/dist/cli/diff-LYNRCJZE.js.map +1 -0
  120. package/dist/cli/doctor-5IBP4R5J.js +28 -0
  121. package/dist/cli/doctor-5IBP4R5J.js.map +1 -0
  122. package/dist/cli/events-QN6KLN2V.js +340 -0
  123. package/dist/cli/events-QN6KLN2V.js.map +1 -0
  124. package/dist/cli/index.js +3500 -0
  125. package/dist/cli/index.js.map +1 -0
  126. package/dist/cli/mcp-FGKEH7RG.js +277 -0
  127. package/dist/cli/mcp-FGKEH7RG.js.map +1 -0
  128. package/dist/cli/mcp-browse-YCND4NWT.js +178 -0
  129. package/dist/cli/mcp-browse-YCND4NWT.js.map +1 -0
  130. package/dist/cli/mcp-inspect-V34J3VX5.js +143 -0
  131. package/dist/cli/mcp-inspect-V34J3VX5.js.map +1 -0
  132. package/dist/cli/package.json +3 -0
  133. package/dist/cli/prompt-I775PNKT.js +16 -0
  134. package/dist/cli/prompt-I775PNKT.js.map +1 -0
  135. package/dist/cli/prune-sessions-KGIIYD3P.js +44 -0
  136. package/dist/cli/prune-sessions-KGIIYD3P.js.map +1 -0
  137. package/dist/cli/replay-RDXLUAOE.js +292 -0
  138. package/dist/cli/replay-RDXLUAOE.js.map +1 -0
  139. package/dist/cli/run-RCAC2RYW.js +223 -0
  140. package/dist/cli/run-RCAC2RYW.js.map +1 -0
  141. package/dist/cli/server-FFU6TLYJ.js +3658 -0
  142. package/dist/cli/server-FFU6TLYJ.js.map +1 -0
  143. package/dist/cli/sessions-QT26MQAE.js +107 -0
  144. package/dist/cli/sessions-QT26MQAE.js.map +1 -0
  145. package/dist/cli/setup-VV4WKXHV.js +767 -0
  146. package/dist/cli/setup-VV4WKXHV.js.map +1 -0
  147. package/dist/cli/stats-JVZPQWAN.js +15 -0
  148. package/dist/cli/stats-JVZPQWAN.js.map +1 -0
  149. package/dist/cli/update-KYI3OVJP.js +15 -0
  150. package/dist/cli/update-KYI3OVJP.js.map +1 -0
  151. package/dist/cli/version-ANYORXTI.js +34 -0
  152. package/dist/cli/version-ANYORXTI.js.map +1 -0
  153. package/dist/index.d.ts +2557 -0
  154. package/dist/index.js +15000 -0
  155. package/dist/index.js.map +1 -0
  156. package/package.json +106 -0
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
3
+ import {
4
+ pricingFor
5
+ } from "./chunk-JMWHXZEL.js";
6
+
7
+ // src/telemetry/stats.ts
8
+ var DEEPSEEK_PRICING = {
9
+ "deepseek-v4-flash": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 },
10
+ "deepseek-v4-pro": { inputCacheHit: 0.139, inputCacheMiss: 1.667, output: 3.333 },
11
+ "deepseek-chat": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 },
12
+ "deepseek-reasoner": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 }
13
+ };
14
+ var CLAUDE_SONNET_PRICING = { input: 3, output: 15 };
15
+ var DEEPSEEK_CONTEXT_TOKENS = {
16
+ "deepseek-v4-flash": 1e6,
17
+ "deepseek-v4-pro": 1e6,
18
+ "deepseek-chat": 1e6,
19
+ "deepseek-reasoner": 1e6
20
+ };
21
+ var DEFAULT_CONTEXT_TOKENS = 131072;
22
+ function costUsd(model, usage) {
23
+ const p = pricingFor(model);
24
+ if (!p || p.inputCacheHit === 0 && p.inputCacheMiss === 0 && p.output === 0) return 0;
25
+ return (usage.promptCacheHitTokens * p.inputCacheHit + usage.promptCacheMissTokens * p.inputCacheMiss + usage.completionTokens * p.output) / 1e6;
26
+ }
27
+ function inputCostUsd(model, usage) {
28
+ const p = pricingFor(model);
29
+ if (!p || p.inputCacheHit === 0 && p.inputCacheMiss === 0) return 0;
30
+ return (usage.promptCacheHitTokens * p.inputCacheHit + usage.promptCacheMissTokens * p.inputCacheMiss) / 1e6;
31
+ }
32
+ function outputCostUsd(model, usage) {
33
+ const p = pricingFor(model);
34
+ if (!p) return 0;
35
+ return usage.completionTokens * p.output / 1e6;
36
+ }
37
+ function cacheSavingsUsd(model, hitTokens) {
38
+ if (hitTokens <= 0) return 0;
39
+ const p = pricingFor(model);
40
+ if (!p) return 0;
41
+ return hitTokens * (p.inputCacheMiss - p.inputCacheHit) / 1e6;
42
+ }
43
+ function claudeEquivalentCost(usage) {
44
+ return (usage.promptTokens * CLAUDE_SONNET_PRICING.input + usage.completionTokens * CLAUDE_SONNET_PRICING.output) / 1e6;
45
+ }
46
+ var SessionStats = class {
47
+ turns = [];
48
+ /** Cost from prior runs of a resumed session, restored from session meta. */
49
+ _carryoverCost = 0;
50
+ /** Turn count from prior runs of a resumed session. */
51
+ _carryoverTurns = 0;
52
+ _carryoverCacheHit = 0;
53
+ _carryoverCacheMiss = 0;
54
+ /** Last turn's promptTokens before exit — surfaced via summary() until the next live turn lands. */
55
+ _carryoverLastPromptTokens = 0;
56
+ /** Seed totals from a resumed session's persisted meta — only call once at construction. */
57
+ seedCarryover(opts) {
58
+ if (typeof opts.totalCostUsd === "number" && opts.totalCostUsd > 0) {
59
+ this._carryoverCost = opts.totalCostUsd;
60
+ }
61
+ if (typeof opts.turnCount === "number" && opts.turnCount > 0) {
62
+ this._carryoverTurns = opts.turnCount;
63
+ }
64
+ if (typeof opts.cacheHitTokens === "number" && opts.cacheHitTokens > 0) {
65
+ this._carryoverCacheHit = opts.cacheHitTokens;
66
+ }
67
+ if (typeof opts.cacheMissTokens === "number" && opts.cacheMissTokens > 0) {
68
+ this._carryoverCacheMiss = opts.cacheMissTokens;
69
+ }
70
+ if (typeof opts.lastPromptTokens === "number" && opts.lastPromptTokens > 0) {
71
+ this._carryoverLastPromptTokens = opts.lastPromptTokens;
72
+ }
73
+ }
74
+ record(turn, model, usage) {
75
+ const cost = costUsd(model, usage);
76
+ const stats = {
77
+ turn,
78
+ model,
79
+ usage,
80
+ cost,
81
+ cacheHitRatio: usage.cacheHitRatio
82
+ };
83
+ this.turns.push(stats);
84
+ return stats;
85
+ }
86
+ get totalCost() {
87
+ return this._carryoverCost + this.turns.reduce((sum, t) => sum + t.cost, 0);
88
+ }
89
+ get totalClaudeEquivalent() {
90
+ return this.turns.reduce((sum, t) => sum + claudeEquivalentCost(t.usage), 0);
91
+ }
92
+ get savingsVsClaude() {
93
+ const c = this.totalClaudeEquivalent;
94
+ return c > 0 ? 1 - this.totalCost / c : 0;
95
+ }
96
+ get totalInputCost() {
97
+ return this.turns.reduce((sum, t) => sum + inputCostUsd(t.model, t.usage), 0);
98
+ }
99
+ get totalOutputCost() {
100
+ return this.turns.reduce((sum, t) => sum + outputCostUsd(t.model, t.usage), 0);
101
+ }
102
+ get aggregateCacheHitRatio() {
103
+ let hit = this._carryoverCacheHit;
104
+ let miss = this._carryoverCacheMiss;
105
+ for (const t of this.turns) {
106
+ hit += t.usage.promptCacheHitTokens;
107
+ miss += t.usage.promptCacheMissTokens;
108
+ }
109
+ const denom = hit + miss;
110
+ return denom > 0 ? hit / denom : 0;
111
+ }
112
+ summary() {
113
+ const last = this.turns[this.turns.length - 1];
114
+ return {
115
+ turns: this.turns.length + this._carryoverTurns,
116
+ totalCostUsd: round(this.totalCost, 6),
117
+ totalInputCostUsd: round(this.totalInputCost, 6),
118
+ totalOutputCostUsd: round(this.totalOutputCost, 6),
119
+ claudeEquivalentUsd: round(this.totalClaudeEquivalent, 6),
120
+ savingsVsClaudePct: round(this.savingsVsClaude * 100, 2),
121
+ cacheHitRatio: round(this.aggregateCacheHitRatio, 4),
122
+ lastPromptTokens: last?.usage.promptTokens ?? this._carryoverLastPromptTokens,
123
+ lastTurnCostUsd: round(last?.cost ?? 0, 6)
124
+ };
125
+ }
126
+ };
127
+ function round(n, digits) {
128
+ const f = 10 ** digits;
129
+ return Math.round(n * f) / f;
130
+ }
131
+
132
+ export {
133
+ DEEPSEEK_PRICING,
134
+ DEEPSEEK_CONTEXT_TOKENS,
135
+ DEFAULT_CONTEXT_TOKENS,
136
+ costUsd,
137
+ inputCostUsd,
138
+ outputCostUsd,
139
+ cacheSavingsUsd,
140
+ claudeEquivalentCost,
141
+ SessionStats
142
+ };
143
+ //# sourceMappingURL=chunk-ANJSUESV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/stats.ts"],"sourcesContent":["import type { Usage } from \"../client.js\";\nimport { pricingFor, contextTokensFor } from \"../providers/registry.js\";\n\n/**\n * @deprecated Use `pricingFor(model)` from the provider registry instead.\n * Kept for backwards-compat — still resolves through provider system.\n */\nexport function deepseekPricingFor(model: string): {\n inputCacheHit: number;\n inputCacheMiss: number;\n output: number;\n} {\n return pricingFor(model);\n}\n\n/**\n * USD per 1M tokens — DeepSeek-specific pricing table.\n * @deprecated Use `pricingFor(model)` from the provider registry.\n * Kept as a snapshot for backwards-compat importers.\n */\nexport const DEEPSEEK_PRICING: Record<\n string,\n { inputCacheHit: number; inputCacheMiss: number; output: number }\n> = {\n \"deepseek-v4-flash\": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 },\n \"deepseek-v4-pro\": { inputCacheHit: 0.139, inputCacheMiss: 1.667, output: 3.333 },\n \"deepseek-chat\": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 },\n \"deepseek-reasoner\": { inputCacheHit: 0.028, inputCacheMiss: 0.139, output: 0.278 },\n};\n\n/** Reference Claude Sonnet 4.6 pricing (USD per 1M tokens). */\nexport const CLAUDE_SONNET_PRICING = { input: 3.0, output: 15.0 };\n\n/**\n * Prompt-side window only; completion caps live server-side and don't affect\n * this gauge.\n * @deprecated Use `contextTokensFor(model)` from the provider registry.\n */\nexport const DEEPSEEK_CONTEXT_TOKENS: Record<string, number> = {\n \"deepseek-v4-flash\": 1_000_000,\n \"deepseek-v4-pro\": 1_000_000,\n \"deepseek-chat\": 1_000_000,\n \"deepseek-reasoner\": 1_000_000,\n};\n\n/** Fallback when the caller's model id isn't in any provider table. */\nexport const DEFAULT_CONTEXT_TOKENS = 131_072;\n\n/** Context window size for a model, resolved through the provider registry. */\nexport function contextTokensForModel(model: string): number {\n return contextTokensFor(model) || DEFAULT_CONTEXT_TOKENS;\n}\n\nexport function costUsd(model: string, usage: Usage): number {\n const p = pricingFor(model);\n if (!p || (p.inputCacheHit === 0 && p.inputCacheMiss === 0 && p.output === 0)) return 0;\n return (\n (usage.promptCacheHitTokens * p.inputCacheHit +\n usage.promptCacheMissTokens * p.inputCacheMiss +\n usage.completionTokens * p.output) /\n 1_000_000\n );\n}\n\n/** Input-side cost only (prompt, cache hit + miss). Used for the panel breakdown. */\nexport function inputCostUsd(model: string, usage: Usage): number {\n const p = pricingFor(model);\n if (!p || (p.inputCacheHit === 0 && p.inputCacheMiss === 0)) return 0;\n return (\n (usage.promptCacheHitTokens * p.inputCacheHit +\n usage.promptCacheMissTokens * p.inputCacheMiss) /\n 1_000_000\n );\n}\n\n/** Output-side cost only (completion tokens). Used for the panel breakdown. */\nexport function outputCostUsd(model: string, usage: Usage): number {\n const p = pricingFor(model);\n if (!p) return 0;\n return (usage.completionTokens * p.output) / 1_000_000;\n}\n\nexport function cacheSavingsUsd(model: string, hitTokens: number): number {\n if (hitTokens <= 0) return 0;\n const p = pricingFor(model);\n if (!p) return 0;\n return (hitTokens * (p.inputCacheMiss - p.inputCacheHit)) / 1_000_000;\n}\n\nexport function claudeEquivalentCost(usage: Usage): number {\n return (\n (usage.promptTokens * CLAUDE_SONNET_PRICING.input +\n usage.completionTokens * CLAUDE_SONNET_PRICING.output) /\n 1_000_000\n );\n}\n\nexport interface TurnStats {\n turn: number;\n model: string;\n usage: Usage;\n cost: number;\n cacheHitRatio: number;\n}\n\nexport interface SessionSummary {\n turns: number;\n totalCostUsd: number;\n totalInputCostUsd: number;\n /** Output-side (completion) cost aggregated across the session. */\n totalOutputCostUsd: number;\n /** @deprecated Claude reference; kept for benchmarks + replay compat, no longer surfaced in the TUI. */\n claudeEquivalentUsd: number;\n /** @deprecated. Same as claudeEquivalentUsd — synthetic ratio, not a real measurement. */\n savingsVsClaudePct: number;\n cacheHitRatio: number;\n /** Floor estimate for next call — actual cost = this + user delta + new tool outputs. */\n lastPromptTokens: number;\n lastTurnCostUsd: number;\n}\n\nexport class SessionStats {\n readonly turns: TurnStats[] = [];\n /** Cost from prior runs of a resumed session, restored from session meta. */\n private _carryoverCost = 0;\n /** Turn count from prior runs of a resumed session. */\n private _carryoverTurns = 0;\n private _carryoverCacheHit = 0;\n private _carryoverCacheMiss = 0;\n /** Last turn's promptTokens before exit — surfaced via summary() until the next live turn lands. */\n private _carryoverLastPromptTokens = 0;\n\n /** Seed totals from a resumed session's persisted meta — only call once at construction. */\n seedCarryover(opts: {\n totalCostUsd?: number;\n turnCount?: number;\n cacheHitTokens?: number;\n cacheMissTokens?: number;\n lastPromptTokens?: number;\n }): void {\n if (typeof opts.totalCostUsd === \"number\" && opts.totalCostUsd > 0) {\n this._carryoverCost = opts.totalCostUsd;\n }\n if (typeof opts.turnCount === \"number\" && opts.turnCount > 0) {\n this._carryoverTurns = opts.turnCount;\n }\n if (typeof opts.cacheHitTokens === \"number\" && opts.cacheHitTokens > 0) {\n this._carryoverCacheHit = opts.cacheHitTokens;\n }\n if (typeof opts.cacheMissTokens === \"number\" && opts.cacheMissTokens > 0) {\n this._carryoverCacheMiss = opts.cacheMissTokens;\n }\n if (typeof opts.lastPromptTokens === \"number\" && opts.lastPromptTokens > 0) {\n this._carryoverLastPromptTokens = opts.lastPromptTokens;\n }\n }\n\n record(turn: number, model: string, usage: Usage): TurnStats {\n const cost = costUsd(model, usage);\n const stats: TurnStats = {\n turn,\n model,\n usage,\n cost,\n cacheHitRatio: usage.cacheHitRatio,\n };\n this.turns.push(stats);\n return stats;\n }\n\n get totalCost(): number {\n return this._carryoverCost + this.turns.reduce((sum, t) => sum + t.cost, 0);\n }\n\n get totalClaudeEquivalent(): number {\n return this.turns.reduce((sum, t) => sum + claudeEquivalentCost(t.usage), 0);\n }\n\n get savingsVsClaude(): number {\n const c = this.totalClaudeEquivalent;\n return c > 0 ? 1 - this.totalCost / c : 0;\n }\n\n get totalInputCost(): number {\n return this.turns.reduce((sum, t) => sum + inputCostUsd(t.model, t.usage), 0);\n }\n\n get totalOutputCost(): number {\n return this.turns.reduce((sum, t) => sum + outputCostUsd(t.model, t.usage), 0);\n }\n\n get aggregateCacheHitRatio(): number {\n let hit = this._carryoverCacheHit;\n let miss = this._carryoverCacheMiss;\n for (const t of this.turns) {\n hit += t.usage.promptCacheHitTokens;\n miss += t.usage.promptCacheMissTokens;\n }\n const denom = hit + miss;\n return denom > 0 ? hit / denom : 0;\n }\n\n summary(): SessionSummary {\n const last = this.turns[this.turns.length - 1];\n return {\n turns: this.turns.length + this._carryoverTurns,\n totalCostUsd: round(this.totalCost, 6),\n totalInputCostUsd: round(this.totalInputCost, 6),\n totalOutputCostUsd: round(this.totalOutputCost, 6),\n claudeEquivalentUsd: round(this.totalClaudeEquivalent, 6),\n savingsVsClaudePct: round(this.savingsVsClaude * 100, 2),\n cacheHitRatio: round(this.aggregateCacheHitRatio, 4),\n lastPromptTokens: last?.usage.promptTokens ?? this._carryoverLastPromptTokens,\n lastTurnCostUsd: round(last?.cost ?? 0, 6),\n };\n }\n}\n\nfunction round(n: number, digits: number): number {\n const f = 10 ** digits;\n return Math.round(n * f) / f;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
3
+ import {
4
+ sanitizeName,
5
+ sessionsDir
6
+ } from "./chunk-Y7XQU2EL.js";
7
+
8
+ // src/adapters/event-sink-jsonl.ts
9
+ import { chmodSync, createWriteStream, mkdirSync } from "fs";
10
+ import { dirname, join } from "path";
11
+ function eventLogPath(sessionName) {
12
+ return join(sessionsDir(), `${sanitizeName(sessionName)}.events.jsonl`);
13
+ }
14
+ var JsonlEventSink = class {
15
+ constructor(stream) {
16
+ this.stream = stream;
17
+ }
18
+ stream;
19
+ buffered = 0;
20
+ append(ev) {
21
+ if (ev.type === "model.delta") return;
22
+ this.stream.write(`${JSON.stringify(ev)}
23
+ `);
24
+ this.buffered++;
25
+ }
26
+ flush() {
27
+ return new Promise((resolve) => {
28
+ if (this.buffered === 0) return resolve();
29
+ this.stream.uncork();
30
+ this.buffered = 0;
31
+ resolve();
32
+ });
33
+ }
34
+ close() {
35
+ return new Promise((resolve) => {
36
+ this.stream.end(() => resolve());
37
+ });
38
+ }
39
+ };
40
+ function openEventSink(path) {
41
+ mkdirSync(dirname(path), { recursive: true });
42
+ const stream = createWriteStream(path, { flags: "a" });
43
+ try {
44
+ chmodSync(path, 384);
45
+ } catch {
46
+ }
47
+ return new JsonlEventSink(stream);
48
+ }
49
+
50
+ export {
51
+ eventLogPath,
52
+ openEventSink
53
+ };
54
+ //# sourceMappingURL=chunk-DB2Z3DKZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/event-sink-jsonl.ts"],"sourcesContent":["import { type WriteStream, chmodSync, createWriteStream, mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { Event } from \"../core/events.js\";\nimport { sanitizeName, sessionsDir } from \"../memory/session.js\";\nimport type { EventSink } from \"../ports/event-sink.js\";\n\nexport function eventLogPath(sessionName: string): string {\n return join(sessionsDir(), `${sanitizeName(sessionName)}.events.jsonl`);\n}\n\nexport class JsonlEventSink implements EventSink {\n private buffered = 0;\n\n constructor(private readonly stream: WriteStream) {}\n\n append(ev: Event): void {\n // Skip model.delta — recoverable from model.final.text, would balloon sidecar.\n if (ev.type === \"model.delta\") return;\n this.stream.write(`${JSON.stringify(ev)}\\n`);\n this.buffered++;\n }\n\n flush(): Promise<void> {\n return new Promise((resolve) => {\n if (this.buffered === 0) return resolve();\n this.stream.uncork();\n this.buffered = 0;\n resolve();\n });\n }\n\n close(): Promise<void> {\n return new Promise((resolve) => {\n this.stream.end(() => resolve());\n });\n }\n}\n\nexport function openEventSink(path: string): JsonlEventSink {\n mkdirSync(dirname(path), { recursive: true });\n const stream = createWriteStream(path, { flags: \"a\" });\n try {\n chmodSync(path, 0o600);\n } catch {\n /* chmod no-op on Windows */\n }\n return new JsonlEventSink(stream);\n}\n"],"mappings":";;;;;;;;AAAA,SAA2B,WAAW,mBAAmB,iBAAiB;AAC1E,SAAS,SAAS,YAAY;AAKvB,SAAS,aAAa,aAA6B;AACxD,SAAO,KAAK,YAAY,GAAG,GAAG,aAAa,WAAW,CAAC,eAAe;AACxE;AAEO,IAAM,iBAAN,MAA0C;AAAA,EAG/C,YAA6B,QAAqB;AAArB;AAAA,EAAsB;AAAA,EAAtB;AAAA,EAFrB,WAAW;AAAA,EAInB,OAAO,IAAiB;AAEtB,QAAI,GAAG,SAAS,cAAe;AAC/B,SAAK,OAAO,MAAM,GAAG,KAAK,UAAU,EAAE,CAAC;AAAA,CAAI;AAC3C,SAAK;AAAA,EACP;AAAA,EAEA,QAAuB;AACrB,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAI,KAAK,aAAa,EAAG,QAAO,QAAQ;AACxC,WAAK,OAAO,OAAO;AACnB,WAAK,WAAW;AAChB,cAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA,EAEA,QAAuB;AACrB,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,WAAK,OAAO,IAAI,MAAM,QAAQ,CAAC;AAAA,IACjC,CAAC;AAAA,EACH;AACF;AAEO,SAAS,cAAc,MAA8B;AAC1D,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,QAAM,SAAS,kBAAkB,MAAM,EAAE,OAAO,IAAI,CAAC;AACrD,MAAI;AACF,cAAU,MAAM,GAAK;AAAA,EACvB,QAAQ;AAAA,EAER;AACA,SAAO,IAAI,eAAe,MAAM;AAClC;","names":[]}
@@ -0,0 +1,400 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
3
+ import {
4
+ createParser
5
+ } from "./chunk-25T6CVUP.js";
6
+ import {
7
+ defaultProvider,
8
+ resolveProvider
9
+ } from "./chunk-JMWHXZEL.js";
10
+
11
+ // src/retry.ts
12
+ var DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504];
13
+ async function fetchWithRetry(fetchFn, url, init, opts = {}) {
14
+ const maxAttempts = opts.maxAttempts ?? 4;
15
+ const initial = opts.initialBackoffMs ?? 500;
16
+ const cap = opts.maxBackoffMs ?? 1e4;
17
+ const retryable = new Set(opts.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES);
18
+ let lastError;
19
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
20
+ if (opts.signal?.aborted) throw new Error("aborted");
21
+ try {
22
+ const resp = await fetchFn(url, init);
23
+ if (resp.ok || !retryable.has(resp.status)) return resp;
24
+ if (attempt === maxAttempts - 1) return resp;
25
+ await resp.text().catch(() => void 0);
26
+ const waitMs = computeWait(attempt, initial, cap, resp.headers.get("Retry-After"));
27
+ opts.onRetry?.({ attempt: attempt + 1, reason: `http ${resp.status}`, waitMs });
28
+ await sleep(waitMs, opts.signal);
29
+ } catch (err) {
30
+ lastError = err;
31
+ if (isAbortError(err) || opts.signal?.aborted) throw err;
32
+ if (attempt === maxAttempts - 1) throw err;
33
+ const waitMs = computeWait(attempt, initial, cap, null);
34
+ opts.onRetry?.({
35
+ attempt: attempt + 1,
36
+ reason: `network: ${messageOf(err)}`,
37
+ waitMs
38
+ });
39
+ await sleep(waitMs, opts.signal);
40
+ }
41
+ }
42
+ throw lastError ?? new Error("fetchWithRetry: loop exited unexpectedly");
43
+ }
44
+ function computeWait(attempt, initial, cap, retryAfter) {
45
+ if (retryAfter) {
46
+ const seconds = Number.parseFloat(retryAfter);
47
+ if (Number.isFinite(seconds) && seconds > 0) {
48
+ return Math.min(seconds * 1e3, cap);
49
+ }
50
+ }
51
+ const exp = initial * 2 ** attempt;
52
+ const jitter = exp * (0.75 + Math.random() * 0.5);
53
+ return Math.min(Math.max(jitter, 0), cap);
54
+ }
55
+ function sleep(ms, signal) {
56
+ if (ms <= 0) return Promise.resolve();
57
+ return new Promise((resolve, reject) => {
58
+ const timer = setTimeout(resolve, ms);
59
+ if (signal) {
60
+ const onAbort = () => {
61
+ clearTimeout(timer);
62
+ reject(new Error("aborted"));
63
+ };
64
+ if (signal.aborted) onAbort();
65
+ else signal.addEventListener("abort", onAbort, { once: true });
66
+ }
67
+ });
68
+ }
69
+ function isAbortError(err) {
70
+ if (!err || typeof err !== "object") return false;
71
+ const name = err.name;
72
+ return name === "AbortError";
73
+ }
74
+ function messageOf(err) {
75
+ if (err instanceof Error) return err.message;
76
+ try {
77
+ return String(err);
78
+ } catch {
79
+ return "unknown error";
80
+ }
81
+ }
82
+
83
+ // src/client.ts
84
+ var Usage = class _Usage {
85
+ constructor(promptTokens = 0, completionTokens = 0, totalTokens = 0, promptCacheHitTokens = 0, promptCacheMissTokens = 0) {
86
+ this.promptTokens = promptTokens;
87
+ this.completionTokens = completionTokens;
88
+ this.totalTokens = totalTokens;
89
+ this.promptCacheHitTokens = promptCacheHitTokens;
90
+ this.promptCacheMissTokens = promptCacheMissTokens;
91
+ }
92
+ promptTokens;
93
+ completionTokens;
94
+ totalTokens;
95
+ promptCacheHitTokens;
96
+ promptCacheMissTokens;
97
+ get cacheHitRatio() {
98
+ const denom = this.promptCacheHitTokens + this.promptCacheMissTokens;
99
+ return denom > 0 ? this.promptCacheHitTokens / denom : 0;
100
+ }
101
+ static fromApi(raw) {
102
+ const u = raw ?? {};
103
+ return new _Usage(
104
+ u.prompt_tokens ?? 0,
105
+ u.completion_tokens ?? 0,
106
+ u.total_tokens ?? 0,
107
+ u.prompt_cache_hit_tokens ?? 0,
108
+ u.prompt_cache_miss_tokens ?? 0
109
+ );
110
+ }
111
+ };
112
+ function pickPrimaryBalance(infos) {
113
+ if (infos.length === 0) return null;
114
+ let best = infos[0];
115
+ for (let i = 1; i < infos.length; i++) {
116
+ if (Number(infos[i].total_balance) > Number(best.total_balance)) best = infos[i];
117
+ }
118
+ return best;
119
+ }
120
+ var OpenAICompatClient = class {
121
+ apiKey;
122
+ baseUrl;
123
+ timeoutMs;
124
+ retry;
125
+ provider;
126
+ _fetch;
127
+ constructor(opts = {}) {
128
+ let provider = opts.provider;
129
+ if (!provider && opts.model) {
130
+ provider = resolveProvider(opts.model);
131
+ }
132
+ if (!provider) {
133
+ provider = defaultProvider();
134
+ }
135
+ this.provider = provider;
136
+ const apiKey = opts.apiKey ?? process.env[provider.auth.envKey] ?? process.env.DEEPSEEK_API_KEY;
137
+ if (!apiKey) {
138
+ throw new Error(
139
+ `${provider.auth.envKey} is not set. Put it in .env or pass apiKey to the client.`
140
+ );
141
+ }
142
+ this.apiKey = apiKey;
143
+ let url = opts.baseUrl ?? process.env.DEEPSEEK_BASE_URL ?? provider.endpoints.chat;
144
+ while (url.endsWith("/")) url = url.slice(0, -1);
145
+ this.baseUrl = url;
146
+ this.timeoutMs = opts.timeoutMs ?? 66e4;
147
+ this._fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);
148
+ this.retry = opts.retry ?? {};
149
+ }
150
+ // -----------------------------------------------------------------------
151
+ // Auth header construction
152
+ // -----------------------------------------------------------------------
153
+ authHeader() {
154
+ const auth = this.provider.auth;
155
+ const header = auth.header ?? "Authorization";
156
+ const prefix = auth.prefix ?? "Bearer ";
157
+ const value = prefix ? `${prefix}${this.apiKey}` : this.apiKey;
158
+ const headers = { [header]: value };
159
+ if (this.provider.extraHeaders) {
160
+ for (const [k, v] of Object.entries(this.provider.extraHeaders)) {
161
+ headers[k] = v;
162
+ }
163
+ }
164
+ return headers;
165
+ }
166
+ // -----------------------------------------------------------------------
167
+ // Payload construction — respects the provider's thinking transport
168
+ // -----------------------------------------------------------------------
169
+ buildPayload(opts, stream) {
170
+ const payload = {
171
+ model: opts.model,
172
+ messages: opts.messages,
173
+ stream
174
+ };
175
+ if (opts.tools?.length) payload.tools = opts.tools;
176
+ if (opts.temperature !== void 0) payload.temperature = opts.temperature;
177
+ if (opts.maxTokens !== void 0) payload.max_tokens = opts.maxTokens;
178
+ if (opts.responseFormat) payload.response_format = opts.responseFormat;
179
+ const thinking = this.provider.thinking;
180
+ const modelSupportsThinking = !thinking.thinkingModels || thinking.thinkingModels.includes(opts.model);
181
+ if (thinking.transport === "extra_body" && opts.thinking && (modelSupportsThinking || opts.thinking === "disabled")) {
182
+ payload.extra_body = {
183
+ ...payload.extra_body ?? {},
184
+ thinking: { type: opts.thinking }
185
+ };
186
+ }
187
+ if (thinking.transport === "reasoning_effort" && opts.reasoningEffort && modelSupportsThinking) {
188
+ payload.reasoning_effort = opts.reasoningEffort;
189
+ }
190
+ if (thinking.transport === "include" && opts.thinking === "enabled" && modelSupportsThinking) {
191
+ payload.thinking = { type: "enabled", budget_tokens: 16e3 };
192
+ }
193
+ if (opts.reasoningEffort && thinking.transport !== "reasoning_effort") {
194
+ payload.reasoning_effort = opts.reasoningEffort;
195
+ }
196
+ if (this.provider.extraBody) {
197
+ for (const [k, v] of Object.entries(this.provider.extraBody)) {
198
+ if (!(k in payload)) payload[k] = v;
199
+ }
200
+ }
201
+ return payload;
202
+ }
203
+ // -----------------------------------------------------------------------
204
+ // Balance
205
+ // -----------------------------------------------------------------------
206
+ /** Returns null on failure so callers can degrade — session must keep working without balance UI. */
207
+ async getBalance(opts = {}) {
208
+ const ep = this.provider.endpoints.balance;
209
+ if (!ep) return null;
210
+ try {
211
+ const resp = await this._fetch(`${this.baseUrl}${ep}`, {
212
+ method: "GET",
213
+ headers: this.authHeader(),
214
+ signal: opts.signal
215
+ });
216
+ if (!resp.ok) return null;
217
+ const data = await resp.json();
218
+ if (!data || !Array.isArray(data.balance_infos)) return null;
219
+ return data;
220
+ } catch {
221
+ return null;
222
+ }
223
+ }
224
+ // -----------------------------------------------------------------------
225
+ // Model listing
226
+ // -----------------------------------------------------------------------
227
+ /** Returns null on failure — callers fall back to the provider's static model list. */
228
+ async listModels(opts = {}) {
229
+ const ep = this.provider.endpoints.models;
230
+ if (!ep) return null;
231
+ try {
232
+ const resp = await this._fetch(`${this.baseUrl}${ep}`, {
233
+ method: "GET",
234
+ headers: this.authHeader(),
235
+ signal: opts.signal
236
+ });
237
+ if (!resp.ok) return null;
238
+ const data = await resp.json();
239
+ if (!data || !Array.isArray(data.data)) return null;
240
+ return data;
241
+ } catch {
242
+ return null;
243
+ }
244
+ }
245
+ // -----------------------------------------------------------------------
246
+ // Chat (non-streaming)
247
+ // -----------------------------------------------------------------------
248
+ async chat(opts) {
249
+ const ctrl = new AbortController();
250
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
251
+ const signal = opts.signal ?? ctrl.signal;
252
+ try {
253
+ const resp = await fetchWithRetry(
254
+ this._fetch,
255
+ `${this.baseUrl}/chat/completions`,
256
+ {
257
+ method: "POST",
258
+ headers: {
259
+ ...this.authHeader(),
260
+ "Content-Type": "application/json"
261
+ },
262
+ body: JSON.stringify(this.buildPayload(opts, false)),
263
+ signal
264
+ },
265
+ { ...this.retry, signal }
266
+ );
267
+ if (!resp.ok) {
268
+ throw new Error(
269
+ `${this.provider.label} ${resp.status}: ${await resp.text()}`
270
+ );
271
+ }
272
+ const data = await resp.json();
273
+ const choice = data.choices?.[0]?.message ?? {};
274
+ return {
275
+ content: choice.content ?? "",
276
+ reasoningContent: choice.reasoning_content ?? null,
277
+ toolCalls: choice.tool_calls ?? [],
278
+ usage: Usage.fromApi(data.usage),
279
+ raw: data
280
+ };
281
+ } finally {
282
+ clearTimeout(timer);
283
+ }
284
+ }
285
+ // -----------------------------------------------------------------------
286
+ // Chat (streaming)
287
+ // -----------------------------------------------------------------------
288
+ async *stream(opts) {
289
+ const ctrl = new AbortController();
290
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
291
+ const signal = opts.signal ?? ctrl.signal;
292
+ let resp;
293
+ try {
294
+ resp = await fetchWithRetry(
295
+ this._fetch,
296
+ `${this.baseUrl}/chat/completions`,
297
+ {
298
+ method: "POST",
299
+ headers: {
300
+ ...this.authHeader(),
301
+ "Content-Type": "application/json",
302
+ Accept: "text/event-stream"
303
+ },
304
+ body: JSON.stringify(this.buildPayload(opts, true)),
305
+ signal
306
+ },
307
+ { ...this.retry, signal }
308
+ );
309
+ } catch (err) {
310
+ clearTimeout(timer);
311
+ throw err;
312
+ }
313
+ if (!resp.ok || !resp.body) {
314
+ clearTimeout(timer);
315
+ throw new Error(
316
+ `${this.provider.label} ${resp.status}: ${await resp.text().catch(() => "")}`
317
+ );
318
+ }
319
+ const queue = [];
320
+ let done = false;
321
+ const parser = createParser({
322
+ onEvent: (ev) => {
323
+ if (!ev.data || ev.data === "[DONE]") {
324
+ done = true;
325
+ return;
326
+ }
327
+ try {
328
+ const json = JSON.parse(ev.data);
329
+ const delta = json.choices?.[0]?.delta ?? {};
330
+ const finishReason = json.choices?.[0]?.finish_reason ?? void 0;
331
+ const chunk = { raw: json, finishReason };
332
+ if (typeof delta.content === "string" && delta.content.length > 0) {
333
+ chunk.contentDelta = delta.content;
334
+ }
335
+ if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
336
+ chunk.reasoningDelta = delta.reasoning_content;
337
+ }
338
+ if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
339
+ const tc = delta.tool_calls[0];
340
+ chunk.toolCallDelta = {
341
+ index: tc.index ?? 0,
342
+ id: tc.id,
343
+ name: tc.function?.name,
344
+ argumentsDelta: tc.function?.arguments
345
+ };
346
+ }
347
+ if (json.usage) {
348
+ chunk.usage = Usage.fromApi(json.usage);
349
+ }
350
+ queue.push(chunk);
351
+ } catch {
352
+ }
353
+ }
354
+ });
355
+ const reader = resp.body.getReader();
356
+ const decoder = new TextDecoder();
357
+ try {
358
+ while (true) {
359
+ if (queue.length > 0) {
360
+ yield queue.shift();
361
+ continue;
362
+ }
363
+ if (done) break;
364
+ const { value, done: streamDone } = await reader.read();
365
+ if (streamDone) break;
366
+ parser.feed(decoder.decode(value, { stream: true }));
367
+ }
368
+ while (queue.length > 0) yield queue.shift();
369
+ } finally {
370
+ clearTimeout(timer);
371
+ reader.releaseLock();
372
+ }
373
+ }
374
+ };
375
+ var DeepSeekClient = class extends OpenAICompatClient {
376
+ constructor(opts = {}) {
377
+ const apiKey = opts.apiKey ?? process.env.DEEPSEEK_API_KEY;
378
+ if (!apiKey) {
379
+ throw new Error(
380
+ "DEEPSEEK_API_KEY is not set. Put it in .env or pass apiKey to DeepSeekClient."
381
+ );
382
+ }
383
+ super({
384
+ apiKey,
385
+ baseUrl: opts.baseUrl,
386
+ timeoutMs: opts.timeoutMs,
387
+ fetch: opts.fetch,
388
+ retry: opts.retry,
389
+ provider: defaultProvider()
390
+ });
391
+ }
392
+ };
393
+
394
+ export {
395
+ Usage,
396
+ pickPrimaryBalance,
397
+ OpenAICompatClient,
398
+ DeepSeekClient
399
+ };
400
+ //# sourceMappingURL=chunk-DDIH3ZAA.js.map