@gamegeek-saikel/dsh-cost-meter 0.1.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.
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/README.zh-CN.md +116 -0
- package/cordis.patch.yml +12 -0
- package/lib/client.js +1481 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +2403 -0
- package/lib/invariant.js +38 -0
- package/lib/types/client/AssistantCostChip.d.ts +29 -0
- package/lib/types/client/AssistantCostChip.d.ts.map +1 -0
- package/lib/types/client/AssistantCostChip.js +75 -0
- package/lib/types/client/CostPluginCard.d.ts +10 -0
- package/lib/types/client/CostPluginCard.d.ts.map +1 -0
- package/lib/types/client/CostPluginCard.js +124 -0
- package/lib/types/client/CostView.d.ts +10 -0
- package/lib/types/client/CostView.d.ts.map +1 -0
- package/lib/types/client/CostView.js +66 -0
- package/lib/types/client/SessionCostLine.d.ts +21 -0
- package/lib/types/client/SessionCostLine.d.ts.map +1 -0
- package/lib/types/client/SessionCostLine.js +173 -0
- package/lib/types/client/SessionCostPill.d.ts +13 -0
- package/lib/types/client/SessionCostPill.d.ts.map +1 -0
- package/lib/types/client/SessionCostPill.js +123 -0
- package/lib/types/client/cost-math.d.ts +88 -0
- package/lib/types/client/cost-math.d.ts.map +1 -0
- package/lib/types/client/cost-math.js +199 -0
- package/lib/types/client/format.d.ts +35 -0
- package/lib/types/client/format.d.ts.map +1 -0
- package/lib/types/client/format.js +61 -0
- package/lib/types/client/index.d.ts +28 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/index.js +59 -0
- package/lib/types/client/locales.d.ts +78 -0
- package/lib/types/client/locales.d.ts.map +1 -0
- package/lib/types/client/locales.js +149 -0
- package/lib/types/index.d.ts +136 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +401 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/invariant.d.ts.map +1 -0
- package/lib/types/invariant.js +38 -0
- package/lib/types/pricebook.d.ts +467 -0
- package/lib/types/pricebook.d.ts.map +1 -0
- package/lib/types/pricebook.js +696 -0
- package/lib/types/pricing.d.ts +93 -0
- package/lib/types/pricing.d.ts.map +1 -0
- package/lib/types/pricing.js +346 -0
- package/lib/types/session-cost-projection.d.ts +70 -0
- package/lib/types/session-cost-projection.d.ts.map +1 -0
- package/lib/types/session-cost-projection.js +198 -0
- package/lib/types/subagent-cost.d.ts +51 -0
- package/lib/types/subagent-cost.d.ts.map +1 -0
- package/lib/types/subagent-cost.js +79 -0
- package/lib/types/types.d.ts +334 -0
- package/lib/types/types.d.ts.map +1 -0
- package/lib/types/types.js +6 -0
- package/package.json +134 -0
- package/src/client/AssistantCostChip.module.css +81 -0
- package/src/client/AssistantCostChip.tsx +109 -0
- package/src/client/CostPluginCard.module.css +186 -0
- package/src/client/CostPluginCard.tsx +264 -0
- package/src/client/CostView.module.css +168 -0
- package/src/client/CostView.tsx +145 -0
- package/src/client/SessionCostLine.module.css +19 -0
- package/src/client/SessionCostLine.tsx +186 -0
- package/src/client/SessionCostPill.module.css +118 -0
- package/src/client/SessionCostPill.tsx +192 -0
- package/src/client/cost-math.ts +230 -0
- package/src/client/format.ts +63 -0
- package/src/client/index.ts +108 -0
- package/src/client/locales.ts +165 -0
- package/src/css-modules.d.ts +4 -0
- package/src/index.ts +489 -0
- package/src/invariant.ts +47 -0
- package/src/pricebook.ts +825 -0
- package/src/pricing.ts +378 -0
- package/src/session-cost-projection.ts +238 -0
- package/src/subagent-cost.ts +105 -0
- package/src/types.ts +364 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** `cost-meter` namespace dictionaries. */
|
|
2
|
+
/** Dictionary namespace owned by this plugin. */
|
|
3
|
+
export declare const NS = "cost-meter";
|
|
4
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
5
|
+
export declare const zh: {
|
|
6
|
+
readonly 'view.cost': "花费";
|
|
7
|
+
readonly 'view.empty': "暂无花费数据";
|
|
8
|
+
readonly 'view.total': "总花费";
|
|
9
|
+
readonly 'view.uncached': "输入(缓存未命中)";
|
|
10
|
+
readonly 'view.cacheRead': "输入(缓存命中)";
|
|
11
|
+
readonly 'view.output': "输出";
|
|
12
|
+
readonly 'view.cacheWriteTokens': "缓存写入";
|
|
13
|
+
readonly 'view.unpriced': "{count} 步无价格";
|
|
14
|
+
readonly 'view.steps': "每轮回复花费";
|
|
15
|
+
readonly 'step.turn': "第 {turn} 轮";
|
|
16
|
+
readonly 'step.model': "模型 {model}";
|
|
17
|
+
readonly 'step.time': "{time}";
|
|
18
|
+
readonly 'step.tokens': "未命中 {uncached} · 缓存读 {read} · 缓存写 {write} · 输出 {output}";
|
|
19
|
+
readonly 'step.cost': "花费 {amount}";
|
|
20
|
+
readonly 'step.snapshot': "快照 · {time}";
|
|
21
|
+
readonly 'step.unpriced': "—({reason})";
|
|
22
|
+
readonly 'reason.NO_MODEL': "无模型信息";
|
|
23
|
+
readonly 'reason.NO_PRICE': "无价格";
|
|
24
|
+
readonly 'cost.label': "本会话 {amount}";
|
|
25
|
+
readonly 'line.uncached': "非缓存输入 {amount}";
|
|
26
|
+
readonly 'line.cacheRead': "缓存输入 {amount}";
|
|
27
|
+
readonly 'line.output': "输出 {amount}";
|
|
28
|
+
readonly 'line.subagent': "子代理 {amount}";
|
|
29
|
+
readonly 'pill.label': "花费 {amount}";
|
|
30
|
+
readonly 'pill.estimate': "预计 {amount}";
|
|
31
|
+
readonly 'pill.estimateNote': "估算";
|
|
32
|
+
readonly 'pill.detail.total': "总花费 {amount}";
|
|
33
|
+
readonly 'pill.detail.snapshot': "价格快照({time})";
|
|
34
|
+
readonly 'pill.detail.model': "模型 {model}";
|
|
35
|
+
readonly 'pill.detail.source': "价格来源 {source}";
|
|
36
|
+
readonly 'source.official': "官方页面";
|
|
37
|
+
readonly 'source.fallback': "内置回退";
|
|
38
|
+
readonly 'source.manual': "手动";
|
|
39
|
+
readonly 'source.openrouter': "OpenRouter";
|
|
40
|
+
readonly 'source.none': "无";
|
|
41
|
+
readonly 'band.peak': "高峰价";
|
|
42
|
+
readonly 'band.offPeak': "闲时价";
|
|
43
|
+
readonly 'band.single': "单价";
|
|
44
|
+
readonly 'price.peakExtra': "高峰 · 比闲时多 {multiplier} 倍";
|
|
45
|
+
readonly 'price.offPeakSaving': "闲时 · 比高峰省 {percent}";
|
|
46
|
+
readonly 'price.peakRatio': "高峰 {multiplier}";
|
|
47
|
+
readonly 'price.offPeakRatio': "闲时 {multiplier}";
|
|
48
|
+
readonly 'chip.title': "本回复花费 {amount}";
|
|
49
|
+
readonly 'chip.unpriced': "—";
|
|
50
|
+
readonly 'balance.label': "余额 {amount}";
|
|
51
|
+
readonly 'balance.failed': "余额不可用";
|
|
52
|
+
readonly 'balance.detail': "余额 {amount}(赠送 {granted} · 充值 {toppedUp})";
|
|
53
|
+
readonly 'balance.suspended': "账户已暂停付费请求";
|
|
54
|
+
readonly refreshedAt: "更新于 {time}";
|
|
55
|
+
readonly 'settings.title': "花费计价";
|
|
56
|
+
readonly 'settings.current': "当前快照 · {source} · {time}";
|
|
57
|
+
readonly 'settings.noSnapshot': "尚无价格快照";
|
|
58
|
+
readonly 'settings.refresh': "立即刷新";
|
|
59
|
+
readonly 'settings.refreshing': "刷新中…";
|
|
60
|
+
readonly 'settings.overrides': "按模型价目表(手动覆盖,元/百万 tokens)";
|
|
61
|
+
readonly 'settings.aliases': "OpenRouter 别名映射";
|
|
62
|
+
readonly 'settings.discount': "缓存折扣系数(OpenRouter)";
|
|
63
|
+
readonly 'settings.fx': "汇率(USD→CNY)";
|
|
64
|
+
readonly 'settings.fx.auto': "自动";
|
|
65
|
+
readonly 'settings.fx.manual': "手动";
|
|
66
|
+
readonly 'settings.balance': "显示余额";
|
|
67
|
+
readonly 'settings.openrouter': "允许 OpenRouter 回退";
|
|
68
|
+
readonly 'settings.save': "保存";
|
|
69
|
+
readonly 'settings.discard': "丢弃";
|
|
70
|
+
readonly 'settings.saved': "已保存";
|
|
71
|
+
readonly 'settings.error': "操作失败:{error}";
|
|
72
|
+
readonly 'settings.jsonHint': "JSON 对象,键为模型,值含 single/offPeak/peak 价格桶";
|
|
73
|
+
};
|
|
74
|
+
/** English dictionary, key-identical to the Chinese source of truth. */
|
|
75
|
+
export declare const en: Record<ConversationCostKey, string>;
|
|
76
|
+
/** Key domain of the `cost-meter` namespace (zh is the source of truth). */
|
|
77
|
+
export type ConversationCostKey = keyof typeof zh;
|
|
78
|
+
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAE3C,iDAAiD;AACjD,eAAO,MAAM,EAAE,eAAe,CAAA;AAE9B,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+EL,CAAA;AAEV,wEAAwE;AACxE,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAyElD,CAAA;AAED,4EAA4E;AAC5E,MAAM,MAAM,mBAAmB,GAAG,MAAM,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/** `cost-meter` namespace dictionaries. */
|
|
2
|
+
/** Dictionary namespace owned by this plugin. */
|
|
3
|
+
export const NS = 'cost-meter';
|
|
4
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
5
|
+
export const zh = {
|
|
6
|
+
// View tab
|
|
7
|
+
'view.cost': '花费',
|
|
8
|
+
'view.empty': '暂无花费数据',
|
|
9
|
+
'view.total': '总花费',
|
|
10
|
+
'view.uncached': '输入(缓存未命中)',
|
|
11
|
+
'view.cacheRead': '输入(缓存命中)',
|
|
12
|
+
'view.output': '输出',
|
|
13
|
+
'view.cacheWriteTokens': '缓存写入',
|
|
14
|
+
'view.unpriced': '{count} 步无价格',
|
|
15
|
+
'view.steps': '每轮回复花费',
|
|
16
|
+
// Step list
|
|
17
|
+
'step.turn': '第 {turn} 轮',
|
|
18
|
+
'step.model': '模型 {model}',
|
|
19
|
+
'step.time': '{time}',
|
|
20
|
+
'step.tokens': '未命中 {uncached} · 缓存读 {read} · 缓存写 {write} · 输出 {output}',
|
|
21
|
+
'step.cost': '花费 {amount}',
|
|
22
|
+
'step.snapshot': '快照 · {time}',
|
|
23
|
+
'step.unpriced': '—({reason})',
|
|
24
|
+
'reason.NO_MODEL': '无模型信息',
|
|
25
|
+
'reason.NO_PRICE': '无价格',
|
|
26
|
+
// Shared amounts
|
|
27
|
+
'cost.label': '本会话 {amount}',
|
|
28
|
+
'line.uncached': '非缓存输入 {amount}',
|
|
29
|
+
'line.cacheRead': '缓存输入 {amount}',
|
|
30
|
+
'line.output': '输出 {amount}',
|
|
31
|
+
'line.subagent': '子代理 {amount}',
|
|
32
|
+
'pill.label': '花费 {amount}',
|
|
33
|
+
'pill.estimate': '预计 {amount}',
|
|
34
|
+
'pill.estimateNote': '估算',
|
|
35
|
+
'pill.detail.total': '总花费 {amount}',
|
|
36
|
+
'pill.detail.snapshot': '价格快照({time})',
|
|
37
|
+
'pill.detail.model': '模型 {model}',
|
|
38
|
+
'pill.detail.source': '价格来源 {source}',
|
|
39
|
+
'source.official': '官方页面',
|
|
40
|
+
'source.fallback': '内置回退',
|
|
41
|
+
'source.manual': '手动',
|
|
42
|
+
'source.openrouter': 'OpenRouter',
|
|
43
|
+
'source.none': '无',
|
|
44
|
+
'band.peak': '高峰价',
|
|
45
|
+
'band.offPeak': '闲时价',
|
|
46
|
+
'band.single': '单价',
|
|
47
|
+
'price.peakExtra': '高峰 · 比闲时多 {multiplier} 倍',
|
|
48
|
+
'price.offPeakSaving': '闲时 · 比高峰省 {percent}',
|
|
49
|
+
'price.peakRatio': '高峰 {multiplier}',
|
|
50
|
+
'price.offPeakRatio': '闲时 {multiplier}',
|
|
51
|
+
// Chip
|
|
52
|
+
'chip.title': '本回复花费 {amount}',
|
|
53
|
+
'chip.unpriced': '—',
|
|
54
|
+
// Balance (inherited posture)
|
|
55
|
+
'balance.label': '余额 {amount}',
|
|
56
|
+
'balance.failed': '余额不可用',
|
|
57
|
+
'balance.detail': '余额 {amount}(赠送 {granted} · 充值 {toppedUp})',
|
|
58
|
+
'balance.suspended': '账户已暂停付费请求',
|
|
59
|
+
'refreshedAt': '更新于 {time}',
|
|
60
|
+
// Plugin configuration card
|
|
61
|
+
'settings.title': '花费计价',
|
|
62
|
+
'settings.current': '当前快照 · {source} · {time}',
|
|
63
|
+
'settings.noSnapshot': '尚无价格快照',
|
|
64
|
+
'settings.refresh': '立即刷新',
|
|
65
|
+
'settings.refreshing': '刷新中…',
|
|
66
|
+
'settings.overrides': '按模型价目表(手动覆盖,元/百万 tokens)',
|
|
67
|
+
'settings.aliases': 'OpenRouter 别名映射',
|
|
68
|
+
'settings.discount': '缓存折扣系数(OpenRouter)',
|
|
69
|
+
'settings.fx': '汇率(USD→CNY)',
|
|
70
|
+
'settings.fx.auto': '自动',
|
|
71
|
+
'settings.fx.manual': '手动',
|
|
72
|
+
'settings.balance': '显示余额',
|
|
73
|
+
'settings.openrouter': '允许 OpenRouter 回退',
|
|
74
|
+
'settings.save': '保存',
|
|
75
|
+
'settings.discard': '丢弃',
|
|
76
|
+
'settings.saved': '已保存',
|
|
77
|
+
'settings.error': '操作失败:{error}',
|
|
78
|
+
'settings.jsonHint': 'JSON 对象,键为模型,值含 single/offPeak/peak 价格桶',
|
|
79
|
+
};
|
|
80
|
+
/** English dictionary, key-identical to the Chinese source of truth. */
|
|
81
|
+
export const en = {
|
|
82
|
+
'view.cost': 'Cost',
|
|
83
|
+
'view.empty': 'No cost data yet',
|
|
84
|
+
'view.total': 'Total',
|
|
85
|
+
'view.uncached': 'Input (cache miss)',
|
|
86
|
+
'view.cacheRead': 'Input (cache hit)',
|
|
87
|
+
'view.output': 'Output',
|
|
88
|
+
'view.cacheWriteTokens': 'Cache write',
|
|
89
|
+
'view.unpriced': '{count} steps unpriced',
|
|
90
|
+
'view.steps': 'Per-reply cost',
|
|
91
|
+
'step.turn': 'Turn {turn}',
|
|
92
|
+
'step.model': 'Model {model}',
|
|
93
|
+
'step.time': '{time}',
|
|
94
|
+
'step.tokens': 'miss {uncached} · read {read} · write {write} · out {output}',
|
|
95
|
+
'step.cost': 'Cost {amount}',
|
|
96
|
+
'step.snapshot': 'snapshot · {time}',
|
|
97
|
+
'step.unpriced': '—({reason})',
|
|
98
|
+
'reason.NO_MODEL': 'no model info',
|
|
99
|
+
'reason.NO_PRICE': 'no price',
|
|
100
|
+
'cost.label': 'Session {amount}',
|
|
101
|
+
'line.uncached': 'Uncached input {amount}',
|
|
102
|
+
'line.cacheRead': 'Cached input {amount}',
|
|
103
|
+
'line.output': 'Output {amount}',
|
|
104
|
+
'line.subagent': 'Subagents {amount}',
|
|
105
|
+
'pill.label': 'Cost {amount}',
|
|
106
|
+
'pill.estimate': '≈ {amount}',
|
|
107
|
+
'pill.estimateNote': 'estimate',
|
|
108
|
+
'pill.detail.total': 'Total {amount}',
|
|
109
|
+
'pill.detail.snapshot': 'Price snapshot ({time})',
|
|
110
|
+
'pill.detail.model': 'Model {model}',
|
|
111
|
+
'pill.detail.source': 'Price source {source}',
|
|
112
|
+
'source.official': 'official page',
|
|
113
|
+
'source.fallback': 'built-in fallback',
|
|
114
|
+
'source.manual': 'manual',
|
|
115
|
+
'source.openrouter': 'OpenRouter',
|
|
116
|
+
'source.none': 'none',
|
|
117
|
+
'band.peak': 'peak',
|
|
118
|
+
'band.offPeak': 'off-peak',
|
|
119
|
+
'band.single': 'list',
|
|
120
|
+
'price.peakExtra': 'peak · {multiplier}× more than off-peak',
|
|
121
|
+
'price.offPeakSaving': 'off-peak · save {percent} vs peak',
|
|
122
|
+
'price.peakRatio': 'peak {multiplier}',
|
|
123
|
+
'price.offPeakRatio': 'off-peak {multiplier}',
|
|
124
|
+
'chip.title': 'This reply cost {amount}',
|
|
125
|
+
'chip.unpriced': '—',
|
|
126
|
+
'balance.label': 'Balance {amount}',
|
|
127
|
+
'balance.failed': 'Balance unavailable',
|
|
128
|
+
'balance.detail': 'Balance {amount} (granted {granted} · topped up {toppedUp})',
|
|
129
|
+
'balance.suspended': 'Account suspended for paid requests',
|
|
130
|
+
'refreshedAt': 'Updated at {time}',
|
|
131
|
+
'settings.title': 'Cost meter',
|
|
132
|
+
'settings.current': 'Current snapshot · {source} · {time}',
|
|
133
|
+
'settings.noSnapshot': 'No price snapshot yet',
|
|
134
|
+
'settings.refresh': 'Refresh now',
|
|
135
|
+
'settings.refreshing': 'Refreshing…',
|
|
136
|
+
'settings.overrides': 'Per-model prices (manual overrides, CNY per 1M tokens)',
|
|
137
|
+
'settings.aliases': 'OpenRouter alias map',
|
|
138
|
+
'settings.discount': 'Cache-read discount (OpenRouter)',
|
|
139
|
+
'settings.fx': 'Exchange rate (USD→CNY)',
|
|
140
|
+
'settings.fx.auto': 'Auto',
|
|
141
|
+
'settings.fx.manual': 'Manual',
|
|
142
|
+
'settings.balance': 'Show balance',
|
|
143
|
+
'settings.openrouter': 'Allow OpenRouter fallback',
|
|
144
|
+
'settings.save': 'Save',
|
|
145
|
+
'settings.discard': 'Discard',
|
|
146
|
+
'settings.saved': 'Saved',
|
|
147
|
+
'settings.error': 'Operation failed: {error}',
|
|
148
|
+
'settings.jsonHint': 'JSON object keyed by model; values carry single/offPeak/peak price buckets',
|
|
149
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-cost-meter host half: serves the DeepSeek account balance
|
|
3
|
+
* (official `GET /user/balance`), the pricebook (persisted, snapshot-anchored
|
|
4
|
+
* per-model pricing over the official page / built-in fallback / OpenRouter
|
|
5
|
+
* chain), and drives the `sessionCost` projection that folds every usage
|
|
6
|
+
* event into an immutable per-step cost ledger.
|
|
7
|
+
*
|
|
8
|
+
* The API key resolves per refresh through the credential seam; the balance
|
|
9
|
+
* is cached for `refreshMs` to stay polite to the provider's rate limits;
|
|
10
|
+
* the pricebook refresh cadence is `pricingRefreshHours`. The `/cost-meter`
|
|
11
|
+
* route serves the balance + pricebook view (GET) and applies settings
|
|
12
|
+
* mutations and manual refreshes (POST, trust-fenced like the balance route).
|
|
13
|
+
*
|
|
14
|
+
* @module @gamegeek-saikel/dsh-cost-meter
|
|
15
|
+
*/
|
|
16
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
17
|
+
import type { IncomingMessage } from 'node:http';
|
|
18
|
+
import z from '@deepseek-ai/schemastery';
|
|
19
|
+
import type { BalanceInfo, BalanceSnapshot, ModelPrice, WireBalanceResponse } from './types.ts';
|
|
20
|
+
export type * from './types.ts';
|
|
21
|
+
export * from './pricing.ts';
|
|
22
|
+
export * from './pricebook.ts';
|
|
23
|
+
export * from './session-cost-projection.ts';
|
|
24
|
+
export * from './subagent-cost.ts';
|
|
25
|
+
/** Stable Cordis plugin name. */
|
|
26
|
+
export declare const name = "cost-meter";
|
|
27
|
+
/** Services required before the route and the projection can register. */
|
|
28
|
+
export declare const inject: string[];
|
|
29
|
+
/** Public DeepSeek API base; the internal endpoint comes from config `baseURL`. */
|
|
30
|
+
export declare const PUBLIC_BASE_URL = "https://api.deepseek.com";
|
|
31
|
+
/** Default credential reference, matching the llm-deepseek adapter. */
|
|
32
|
+
export declare const DEFAULT_API_KEY_ENV = "DEEPSEEK_API_KEY";
|
|
33
|
+
/** How long a successful balance snapshot stays fresh (milliseconds). */
|
|
34
|
+
export declare const DEFAULT_REFRESH_MS = 60000;
|
|
35
|
+
/** Default cadence for re-fetching the official pricing page (hours). */
|
|
36
|
+
export declare const DEFAULT_PRICING_REFRESH_HOURS = 6;
|
|
37
|
+
/** Plugin config: every field is optional in yml and has a sensible default. */
|
|
38
|
+
export interface Config {
|
|
39
|
+
/** Credential reference resolved per refresh; defaults to `DEEPSEEK_API_KEY`. */
|
|
40
|
+
apiKeyEnv?: string;
|
|
41
|
+
/** Endpoint base; the balance path `/user/balance` is appended. Defaults to the public API. */
|
|
42
|
+
baseURL?: string;
|
|
43
|
+
/** Balance cache lifetime in milliseconds; defaults to 60s. */
|
|
44
|
+
refreshMs?: number;
|
|
45
|
+
/** Hours between official-pricing page refreshes; defaults to 6. */
|
|
46
|
+
pricingRefreshHours?: number;
|
|
47
|
+
/** Non-loopback authorities this deployment serves, mirroring client-connection trustedHosts. */
|
|
48
|
+
trustedHosts?: string[];
|
|
49
|
+
/** Free exchange-rate endpoint (USD base, `rates.CNY`); defaults to open.er-api.com. */
|
|
50
|
+
fxApiUrl?: string;
|
|
51
|
+
/** Fallback USD→CNY rate when auto-fetch never succeeded; defaults to 7.2. */
|
|
52
|
+
defaultFxRate?: number;
|
|
53
|
+
/** OpenRouter models directory; defaults to openrouter.ai. */
|
|
54
|
+
openRouterModelsUrl?: string;
|
|
55
|
+
/** Whether OpenRouter is allowed as a fallback price source; defaults to true. */
|
|
56
|
+
openRouterEnabled?: boolean;
|
|
57
|
+
/** Cap on the persisted snapshot history; defaults to 50. */
|
|
58
|
+
snapshotHistoryLimit?: number;
|
|
59
|
+
/** Manual per-model price overrides (the settings section also edits these). */
|
|
60
|
+
overrides?: Record<string, ModelPrice>;
|
|
61
|
+
/** OpenRouter model-id → canonical key aliases (the settings section also edits these). */
|
|
62
|
+
aliases?: Record<string, string>;
|
|
63
|
+
/** Cache-read price as a fraction of the uncached input price (OpenRouter only). */
|
|
64
|
+
cacheReadDiscount?: number;
|
|
65
|
+
/** Exchange-rate mode: auto-fetch or a manually pinned rate. */
|
|
66
|
+
fxMode?: 'auto' | 'manual';
|
|
67
|
+
/** Manually pinned USD→CNY rate (used in manual mode, auto-mode fallback). */
|
|
68
|
+
manualRate?: number;
|
|
69
|
+
/** Whether the balance readout is shown in the UI. */
|
|
70
|
+
balanceEnabled?: boolean;
|
|
71
|
+
}
|
|
72
|
+
export declare const Config: z<Config>;
|
|
73
|
+
/** Resolved plugin facts after schema defaults. */
|
|
74
|
+
export interface ResolvedConfig {
|
|
75
|
+
apiKeyEnv: string;
|
|
76
|
+
baseURL: string;
|
|
77
|
+
refreshMs: number;
|
|
78
|
+
pricingRefreshHours: number;
|
|
79
|
+
trustedHosts: readonly string[];
|
|
80
|
+
fxApiUrl: string;
|
|
81
|
+
defaultFxRate: number;
|
|
82
|
+
openRouterModelsUrl: string;
|
|
83
|
+
openRouterEnabled: boolean;
|
|
84
|
+
snapshotHistoryLimit: number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Whether a normalized URL hostname names the local loopback authority.
|
|
88
|
+
* @param hostname - WHATWG URL hostname (IPv6 literals retain brackets).
|
|
89
|
+
* @returns true for localhost, IPv6 loopback, or any IPv4 address in 127/8.
|
|
90
|
+
*/
|
|
91
|
+
export declare function isLoopbackHostname(hostname: string): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Browser-trust fence for the balance route, mirroring the /api fence: the
|
|
94
|
+
* Host header is the one header DNS rebinding cannot forge, and loopback or
|
|
95
|
+
* a declared trusted authority passes.
|
|
96
|
+
* @param headers - request headers.
|
|
97
|
+
* @param trustedHosts - non-loopback authorities this deployment serves.
|
|
98
|
+
* @returns true when the request Host is ours.
|
|
99
|
+
*/
|
|
100
|
+
export declare function isTrustedRequest(headers: IncomingMessage['headers'], trustedHosts: readonly string[]): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the API key for one refresh: the credential seam first, the
|
|
103
|
+
* process environment as fallback when no seam is composed.
|
|
104
|
+
* @param ctx - host context.
|
|
105
|
+
* @param env - the credential reference name.
|
|
106
|
+
* @returns the resolved key, or undefined when absent everywhere.
|
|
107
|
+
*/
|
|
108
|
+
export declare function resolveApiKey(ctx: Context, env: string): Promise<string | undefined>;
|
|
109
|
+
/**
|
|
110
|
+
* Parse the first balance row, converting the provider's decimal strings to
|
|
111
|
+
* numbers.
|
|
112
|
+
* @param wire - the parsed provider response.
|
|
113
|
+
* @returns the first row, or null when the response carried no rows.
|
|
114
|
+
*/
|
|
115
|
+
export declare function pickBalanceInfo(wire: WireBalanceResponse): BalanceInfo | null;
|
|
116
|
+
/**
|
|
117
|
+
* One provider balance call: resolve the key, GET /user/balance, and fold
|
|
118
|
+
* the result into a {@link BalanceSnapshot}. Never throws — every failure
|
|
119
|
+
* becomes an ok:false snapshot.
|
|
120
|
+
* @param ctx - host context.
|
|
121
|
+
* @param config - resolved plugin facts.
|
|
122
|
+
* @returns the folded snapshot.
|
|
123
|
+
*/
|
|
124
|
+
export declare function fetchBalance(ctx: Context, config: ResolvedConfig): Promise<BalanceSnapshot>;
|
|
125
|
+
/** The `cost-meter` settings namespace (plugin configuration page). */
|
|
126
|
+
export declare const SETTINGS_NAMESPACE: Branded<"SettingsNamespace">;
|
|
127
|
+
/**
|
|
128
|
+
* Mount the plugin: open the pricebook domain, refresh every remote source,
|
|
129
|
+
* register the `sessionCost` projection, register the /cost-meter
|
|
130
|
+
* route (GET view + POST settings/refresh), and keep the balance and the
|
|
131
|
+
* official pricing fresh.
|
|
132
|
+
* @param ctx - host context.
|
|
133
|
+
* @param config - plugin config (schema defaults applied by the Loader).
|
|
134
|
+
*/
|
|
135
|
+
export declare function apply(ctx: Context, config?: Config): Promise<void>;
|
|
136
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|