@skillfm/local 2.7.2 → 2.7.4
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/dist/checkup/daily-push.d.ts +29 -0
- package/dist/checkup/daily-push.d.ts.map +1 -0
- package/dist/checkup/daily-push.js +86 -0
- package/dist/checkup/daily-push.js.map +1 -0
- package/dist/checkup/dimension-1-usage.d.ts +22 -0
- package/dist/checkup/dimension-1-usage.d.ts.map +1 -0
- package/dist/checkup/dimension-1-usage.js +115 -0
- package/dist/checkup/dimension-1-usage.js.map +1 -0
- package/dist/checkup/dimension-3-files.d.ts +23 -0
- package/dist/checkup/dimension-3-files.d.ts.map +1 -0
- package/dist/checkup/dimension-3-files.js +165 -0
- package/dist/checkup/dimension-3-files.js.map +1 -0
- package/dist/checkup/dimension-4-context.d.ts +18 -0
- package/dist/checkup/dimension-4-context.d.ts.map +1 -0
- package/dist/checkup/dimension-4-context.js +177 -0
- package/dist/checkup/dimension-4-context.js.map +1 -0
- package/dist/checkup/index.d.ts +11 -0
- package/dist/checkup/index.d.ts.map +1 -0
- package/dist/checkup/index.js +10 -0
- package/dist/checkup/index.js.map +1 -0
- package/dist/checkup/report.d.ts +21 -0
- package/dist/checkup/report.d.ts.map +1 -0
- package/dist/checkup/report.js +133 -0
- package/dist/checkup/report.js.map +1 -0
- package/dist/checkup/score.d.ts +27 -0
- package/dist/checkup/score.d.ts.map +1 -0
- package/dist/checkup/score.js +101 -0
- package/dist/checkup/score.js.map +1 -0
- package/dist/checkup/types.d.ts +64 -0
- package/dist/checkup/types.d.ts.map +1 -0
- package/dist/checkup/types.js +5 -0
- package/dist/checkup/types.js.map +1 -0
- package/dist/connectors/anthropic.d.ts.map +1 -1
- package/dist/connectors/anthropic.js +3 -0
- package/dist/connectors/anthropic.js.map +1 -1
- package/dist/guard/bin.js +0 -0
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +27 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/tools/checkup.d.ts +21 -0
- package/dist/mcp/tools/checkup.d.ts.map +1 -0
- package/dist/mcp/tools/checkup.js +35 -0
- package/dist/mcp/tools/checkup.js.map +1 -0
- package/dist/mcp/tools/explain-provider-key.d.ts.map +1 -1
- package/dist/mcp/tools/explain-provider-key.js +15 -13
- package/dist/mcp/tools/explain-provider-key.js.map +1 -1
- package/dist/mcp/tools/index.d.ts +6 -0
- package/dist/mcp/tools/index.d.ts.map +1 -1
- package/dist/mcp/tools/index.js +56 -4
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/setup-gateway.d.ts +16 -0
- package/dist/mcp/tools/setup-gateway.d.ts.map +1 -0
- package/dist/mcp/tools/setup-gateway.js +79 -0
- package/dist/mcp/tools/setup-gateway.js.map +1 -0
- package/dist/mcp/tools/show-my-usage.d.ts +26 -0
- package/dist/mcp/tools/show-my-usage.d.ts.map +1 -0
- package/dist/mcp/tools/show-my-usage.js +68 -0
- package/dist/mcp/tools/show-my-usage.js.map +1 -0
- package/dist/mcp-stdio/bin.js +0 -0
- package/dist/reconciliation/engine.d.ts +30 -0
- package/dist/reconciliation/engine.d.ts.map +1 -0
- package/dist/reconciliation/engine.js +62 -0
- package/dist/reconciliation/engine.js.map +1 -0
- package/dist/reconciliation/index.d.ts +8 -0
- package/dist/reconciliation/index.d.ts.map +1 -0
- package/dist/reconciliation/index.js +8 -0
- package/dist/reconciliation/index.js.map +1 -0
- package/dist/reconciliation/l1-reconciler.d.ts +29 -0
- package/dist/reconciliation/l1-reconciler.d.ts.map +1 -0
- package/dist/reconciliation/l1-reconciler.js +144 -0
- package/dist/reconciliation/l1-reconciler.js.map +1 -0
- package/dist/reconciliation/l2-reconciler.d.ts +21 -0
- package/dist/reconciliation/l2-reconciler.d.ts.map +1 -0
- package/dist/reconciliation/l2-reconciler.js +241 -0
- package/dist/reconciliation/l2-reconciler.js.map +1 -0
- package/dist/reconciliation/types.d.ts +49 -0
- package/dist/reconciliation/types.d.ts.map +1 -0
- package/dist/reconciliation/types.js +9 -0
- package/dist/reconciliation/types.js.map +1 -0
- package/dist/save-token/e1-router.d.ts +7 -0
- package/dist/save-token/e1-router.d.ts.map +1 -0
- package/dist/save-token/e1-router.js +112 -0
- package/dist/save-token/e1-router.js.map +1 -0
- package/dist/save-token/e2-cache.d.ts +7 -0
- package/dist/save-token/e2-cache.d.ts.map +1 -0
- package/dist/save-token/e2-cache.js +128 -0
- package/dist/save-token/e2-cache.js.map +1 -0
- package/dist/save-token/e3-batch.d.ts +7 -0
- package/dist/save-token/e3-batch.d.ts.map +1 -0
- package/dist/save-token/e3-batch.js +80 -0
- package/dist/save-token/e3-batch.js.map +1 -0
- package/dist/save-token/gateway-setup.d.ts +27 -0
- package/dist/save-token/gateway-setup.d.ts.map +1 -0
- package/dist/save-token/gateway-setup.js +200 -0
- package/dist/save-token/gateway-setup.js.map +1 -0
- package/dist/save-token/index.d.ts +13 -0
- package/dist/save-token/index.d.ts.map +1 -0
- package/dist/save-token/index.js +23 -0
- package/dist/save-token/index.js.map +1 -0
- package/dist/save-token/types.d.ts +46 -0
- package/dist/save-token/types.d.ts.map +1 -0
- package/dist/save-token/types.js +5 -0
- package/dist/save-token/types.js.map +1 -0
- package/dist/usage-local/index.d.ts +32 -0
- package/dist/usage-local/index.d.ts.map +1 -0
- package/dist/usage-local/index.js +32 -0
- package/dist/usage-local/index.js.map +1 -0
- package/dist/usage-local/model-pricing.d.ts +38 -0
- package/dist/usage-local/model-pricing.d.ts.map +1 -0
- package/dist/usage-local/model-pricing.js +230 -0
- package/dist/usage-local/model-pricing.js.map +1 -0
- package/dist/usage-local/openclaw-watcher.d.ts +35 -0
- package/dist/usage-local/openclaw-watcher.d.ts.map +1 -0
- package/dist/usage-local/openclaw-watcher.js +190 -0
- package/dist/usage-local/openclaw-watcher.js.map +1 -0
- package/dist/usage-local/store.d.ts +50 -0
- package/dist/usage-local/store.d.ts.map +1 -0
- package/dist/usage-local/store.js +201 -0
- package/dist/usage-local/store.js.map +1 -0
- package/dist/usage-local/types.d.ts +94 -0
- package/dist/usage-local/types.d.ts.map +1 -0
- package/dist/usage-local/types.js +6 -0
- package/dist/usage-local/types.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// sdk/skillfm-local/src/save-token/e2-cache.ts
|
|
2
|
+
//
|
|
3
|
+
// E2 Prompt Caching 自动启用 (PRD §6.3) — Free 即得真省钱.
|
|
4
|
+
//
|
|
5
|
+
// Free: 扫 trajectory 找 system prompt 重复率高 / 长 prompt → surface "启 cache 估省 ¥X"
|
|
6
|
+
// 给 cache_control 代码片段让用户手动加
|
|
7
|
+
// Pro: gateway hook auto inject cache_control breakpoint (2.7.5+)
|
|
8
|
+
//
|
|
9
|
+
// 真证据 (PRD §6.3):
|
|
10
|
+
// OpenAI 50% off cached tokens (auto, 1024+ token)
|
|
11
|
+
// Anthropic 90% off cache reads ($0.30/M vs $3.00/M, 显式 ephemeral)
|
|
12
|
+
// 真案例 1 个开发者 $720 → $72/月 = 90% off
|
|
13
|
+
import { getModelPrice, USD_TO_CNY } from '../usage-local/model-pricing.js';
|
|
14
|
+
import { timeWindow } from '../usage-local/store.js';
|
|
15
|
+
/**
|
|
16
|
+
* 哪些 provider 真支持 prompt cache
|
|
17
|
+
*/
|
|
18
|
+
const CACHE_SUPPORTED_PROVIDERS = new Set(['anthropic', 'openai', 'deepseek']);
|
|
19
|
+
/**
|
|
20
|
+
* 扫描 + 返建议
|
|
21
|
+
*/
|
|
22
|
+
export async function scanE2Cache(store, opts) {
|
|
23
|
+
const window = opts.start && opts.end
|
|
24
|
+
? { start: opts.start, end: opts.end }
|
|
25
|
+
: timeWindow('week');
|
|
26
|
+
const records = store.query(window);
|
|
27
|
+
if (records.length === 0)
|
|
28
|
+
return [];
|
|
29
|
+
// 按 (provider, model) 聚合 cache 利用率
|
|
30
|
+
const byModel = new Map();
|
|
31
|
+
for (const r of records) {
|
|
32
|
+
const key = `${r.provider}/${r.model_id}`;
|
|
33
|
+
const cur = byModel.get(key) ?? {
|
|
34
|
+
count: 0, input_tokens: 0, cache_read_tokens: 0, cache_write_tokens: 0, cost_usd: 0,
|
|
35
|
+
};
|
|
36
|
+
cur.count += 1;
|
|
37
|
+
cur.input_tokens += r.input_tokens;
|
|
38
|
+
cur.cache_read_tokens += r.cache_read_tokens;
|
|
39
|
+
cur.cache_write_tokens += r.cache_write_tokens;
|
|
40
|
+
cur.cost_usd += r.estimated_cost_usd;
|
|
41
|
+
byModel.set(key, cur);
|
|
42
|
+
}
|
|
43
|
+
const suggestions = [];
|
|
44
|
+
for (const [key, agg] of byModel) {
|
|
45
|
+
const [provider] = key.split('/');
|
|
46
|
+
if (!CACHE_SUPPORTED_PROVIDERS.has(provider))
|
|
47
|
+
continue;
|
|
48
|
+
if (agg.cost_usd < 0.5)
|
|
49
|
+
continue;
|
|
50
|
+
if (agg.count < 5)
|
|
51
|
+
continue; // 5+ 次调用才有 cache 意义
|
|
52
|
+
const totalInput = agg.input_tokens + agg.cache_read_tokens;
|
|
53
|
+
if (totalInput < 5000)
|
|
54
|
+
continue; // 累计 input 太少不值得 cache
|
|
55
|
+
const cache_hit_rate = totalInput > 0 ? agg.cache_read_tokens / totalInput : 0;
|
|
56
|
+
// 如果 cache hit rate < 30% + 平均 input > 1024 tokens (cache 阈值) → 推荐启
|
|
57
|
+
const avg_input = agg.input_tokens / agg.count;
|
|
58
|
+
if (cache_hit_rate >= 0.3)
|
|
59
|
+
continue; // 已经在用 cache, skip
|
|
60
|
+
if (avg_input < 1024 && provider === 'openai')
|
|
61
|
+
continue; // OpenAI auto cache 阈值
|
|
62
|
+
if (avg_input < 200)
|
|
63
|
+
continue; // 太短不值得显式 cache
|
|
64
|
+
// 估算启 cache 后省多少
|
|
65
|
+
// 假设 cache hit rate 提到 70% (合理水平)
|
|
66
|
+
const targetCacheRate = 0.7;
|
|
67
|
+
const newCacheRead = totalInput * targetCacheRate;
|
|
68
|
+
const newRawInput = totalInput * (1 - targetCacheRate);
|
|
69
|
+
const price = getModelPrice(provider, key.split('/').slice(1).join('/'));
|
|
70
|
+
const oldCost = (agg.input_tokens / 1_000_000) * price.input_per_1m
|
|
71
|
+
+ (agg.cache_read_tokens / 1_000_000) * price.cache_read_per_1m;
|
|
72
|
+
const newCost = (newRawInput / 1_000_000) * price.input_per_1m
|
|
73
|
+
+ (newCacheRead / 1_000_000) * price.cache_read_per_1m
|
|
74
|
+
+ (totalInput * 0.1 / 1_000_000) * price.cache_write_per_1m; // 估 cache write 是 input 10%
|
|
75
|
+
const savings_usd = oldCost - newCost;
|
|
76
|
+
if (savings_usd <= 0)
|
|
77
|
+
continue;
|
|
78
|
+
const savings_pct = (savings_usd / oldCost) * 100;
|
|
79
|
+
const monthly_savings_usd = savings_usd * (30 / 7);
|
|
80
|
+
const fix_method = opts.is_pro ? 'pro_auto' : 'free_manual';
|
|
81
|
+
suggestions.push({
|
|
82
|
+
engine: 'E2_cache',
|
|
83
|
+
headline: `${key} ${agg.count} 次调用 cache 命中率 ${(cache_hit_rate * 100).toFixed(0)}% — 启 cache 可省 ${Math.round(savings_pct)}% input cost`,
|
|
84
|
+
estimated_savings_pct: Math.round(savings_pct),
|
|
85
|
+
estimated_savings_tokens: Math.round(totalInput * (30 / 7) * targetCacheRate),
|
|
86
|
+
estimated_savings_cny: Math.round(monthly_savings_usd * USD_TO_CNY * 100) / 100,
|
|
87
|
+
fix_method,
|
|
88
|
+
fix_instructions: opts.is_pro ? undefined : buildCacheInstructions(provider),
|
|
89
|
+
pro_unlock_label: opts.is_pro
|
|
90
|
+
? undefined
|
|
91
|
+
: `Pro 升级后 SkillFM gateway auto inject cache_control — 估月省 ¥${Math.round(monthly_savings_usd * USD_TO_CNY)}`,
|
|
92
|
+
details: {
|
|
93
|
+
model: key,
|
|
94
|
+
avg_input_tokens: Math.round(avg_input),
|
|
95
|
+
current_cache_hit_rate: cache_hit_rate,
|
|
96
|
+
target_cache_hit_rate: targetCacheRate,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return suggestions.sort((a, b) => b.estimated_savings_cny - a.estimated_savings_cny);
|
|
101
|
+
}
|
|
102
|
+
function buildCacheInstructions(provider) {
|
|
103
|
+
switch (provider) {
|
|
104
|
+
case 'anthropic':
|
|
105
|
+
return [
|
|
106
|
+
'在 system prompt 里加 cache_control breakpoint:',
|
|
107
|
+
'```',
|
|
108
|
+
'{role: "system", content: [{',
|
|
109
|
+
' type: "text",',
|
|
110
|
+
' text: "你的长 system prompt...",',
|
|
111
|
+
' cache_control: {type: "ephemeral"}', // ephemeral 5 min cache
|
|
112
|
+
'}]}',
|
|
113
|
+
'```',
|
|
114
|
+
'价: cache write +25%, cache read 90% off (vs 普通 input)',
|
|
115
|
+
].join('\n');
|
|
116
|
+
case 'openai':
|
|
117
|
+
return [
|
|
118
|
+
'OpenAI 自动 cache (1024+ token) — 无需 code 改, 但要保证 prompt 前缀稳定:',
|
|
119
|
+
'- 不要每次 inject 时间戳 / 随机 ID 在 prompt 顶',
|
|
120
|
+
'- 把 system + few-shot 例子放最前 (这样 prefix 稳定 → 自动 cache hit)',
|
|
121
|
+
].join('\n');
|
|
122
|
+
case 'deepseek':
|
|
123
|
+
return 'DeepSeek 类似 OpenAI 自动 prefix cache, 保证 prompt 前缀稳定即可';
|
|
124
|
+
default:
|
|
125
|
+
return '该 provider 暂不支持 prompt cache';
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=e2-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2-cache.js","sourceRoot":"","sources":["../../src/save-token/e2-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LocalUsageStore } from '../usage-local/store.js';
|
|
2
|
+
import type { SaveTokenSuggestion, SaveTokenScanOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* 检测 batch 候选: 深夜调用 (00:00-06:00) 或 短时间内 5+ 调用 (批量类)
|
|
5
|
+
*/
|
|
6
|
+
export declare function scanE3Batch(store: LocalUsageStore, opts: SaveTokenScanOptions): Promise<SaveTokenSuggestion[]>;
|
|
7
|
+
//# sourceMappingURL=e3-batch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e3-batch.d.ts","sourceRoot":"","sources":["../../src/save-token/e3-batch.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAO5E;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CA+DhC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// sdk/skillfm-local/src/save-token/e3-batch.ts
|
|
2
|
+
//
|
|
3
|
+
// E3 Batch API 推荐 (PRD §6.4) — 50% 即得 (异步任务).
|
|
4
|
+
//
|
|
5
|
+
// Free: 找 latency-tolerant task (深夜跑 / 批量同类 / 长完成时间) → 提示走 batch
|
|
6
|
+
// Pro: gateway 自动 dispatch batch (2.7.5+)
|
|
7
|
+
//
|
|
8
|
+
// 真证据:
|
|
9
|
+
// OpenAI Batch API 50% off all models, 24h window
|
|
10
|
+
// Anthropic Message Batches 同 50%
|
|
11
|
+
// Stack: cache + batch = 75-95% off
|
|
12
|
+
import { USD_TO_CNY } from '../usage-local/model-pricing.js';
|
|
13
|
+
import { timeWindow } from '../usage-local/store.js';
|
|
14
|
+
/**
|
|
15
|
+
* Batch API 真支持 provider
|
|
16
|
+
*/
|
|
17
|
+
const BATCH_SUPPORTED_PROVIDERS = new Set(['openai', 'anthropic']);
|
|
18
|
+
/**
|
|
19
|
+
* 检测 batch 候选: 深夜调用 (00:00-06:00) 或 短时间内 5+ 调用 (批量类)
|
|
20
|
+
*/
|
|
21
|
+
export async function scanE3Batch(store, opts) {
|
|
22
|
+
const window = opts.start && opts.end
|
|
23
|
+
? { start: opts.start, end: opts.end }
|
|
24
|
+
: timeWindow('week');
|
|
25
|
+
const records = store.query(window);
|
|
26
|
+
if (records.length === 0)
|
|
27
|
+
return [];
|
|
28
|
+
// 找深夜调用 (00:00-06:00 in Asia/Shanghai)
|
|
29
|
+
const nightRecords = records.filter((r) => {
|
|
30
|
+
if (!BATCH_SUPPORTED_PROVIDERS.has(r.provider.toLowerCase()))
|
|
31
|
+
return false;
|
|
32
|
+
const d = new Date(r.timestamp);
|
|
33
|
+
// 转 Asia/Shanghai 小时 (UTC+8)
|
|
34
|
+
const shanghaiHour = (d.getUTCHours() + 8) % 24;
|
|
35
|
+
return shanghaiHour < 6 || shanghaiHour >= 23;
|
|
36
|
+
});
|
|
37
|
+
if (nightRecords.length === 0)
|
|
38
|
+
return [];
|
|
39
|
+
// 算这些 record 总成本 + 估省 50%
|
|
40
|
+
let total_cost_usd = 0;
|
|
41
|
+
let total_tokens = 0;
|
|
42
|
+
const providerCount = new Map();
|
|
43
|
+
for (const r of nightRecords) {
|
|
44
|
+
total_cost_usd += r.estimated_cost_usd;
|
|
45
|
+
total_tokens += r.input_tokens + r.output_tokens;
|
|
46
|
+
providerCount.set(r.provider, (providerCount.get(r.provider) ?? 0) + 1);
|
|
47
|
+
}
|
|
48
|
+
if (total_cost_usd < 0.5)
|
|
49
|
+
return []; // 太少不值得
|
|
50
|
+
const savings_usd = total_cost_usd * 0.5;
|
|
51
|
+
const monthly_savings_usd = savings_usd * (30 / 7);
|
|
52
|
+
const monthly_savings_tokens = total_tokens * (30 / 7);
|
|
53
|
+
const providerList = Array.from(providerCount.entries())
|
|
54
|
+
.map(([p, c]) => `${p} ${c} 次`)
|
|
55
|
+
.join(', ');
|
|
56
|
+
const fix_method = opts.is_pro ? 'pro_auto' : 'free_manual';
|
|
57
|
+
return [{
|
|
58
|
+
engine: 'E3_batch',
|
|
59
|
+
headline: `深夜调用 ${nightRecords.length} 次 (${providerList}, ¥${(total_cost_usd * USD_TO_CNY).toFixed(2)}) — 走 batch API 50% off`,
|
|
60
|
+
estimated_savings_pct: 50,
|
|
61
|
+
estimated_savings_tokens: Math.round(monthly_savings_tokens),
|
|
62
|
+
estimated_savings_cny: Math.round(monthly_savings_usd * USD_TO_CNY * 100) / 100,
|
|
63
|
+
fix_method,
|
|
64
|
+
fix_instructions: opts.is_pro ? undefined : [
|
|
65
|
+
'深夜任务 (你不在等结果) 改走 Batch API 即 50% off:',
|
|
66
|
+
'- OpenAI: POST /v1/batches + 上传 JSONL, 24h 内返结果 (50% off)',
|
|
67
|
+
'- Anthropic: POST /v1/messages/batches 同上',
|
|
68
|
+
'在你的 agent / script 里, 把这类异步任务排队上传, 而非实时调用',
|
|
69
|
+
].join('\n'),
|
|
70
|
+
pro_unlock_label: opts.is_pro
|
|
71
|
+
? undefined
|
|
72
|
+
: `Pro 升级后 SkillFM gateway 自动 dispatch latency-tolerant task 到 Batch API — 估月省 ¥${Math.round(monthly_savings_usd * USD_TO_CNY)}`,
|
|
73
|
+
details: {
|
|
74
|
+
night_call_count: nightRecords.length,
|
|
75
|
+
provider_breakdown: Object.fromEntries(providerCount),
|
|
76
|
+
total_cost_usd_window: total_cost_usd,
|
|
77
|
+
},
|
|
78
|
+
}];
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=e3-batch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e3-batch.js","sourceRoot":"","sources":["../../src/save-token/e3-batch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { GatewayDetection } from './types.js';
|
|
2
|
+
export declare const SKILLFM_GATEWAY_BASE_URL: string;
|
|
3
|
+
/**
|
|
4
|
+
* 检测各 IDE 的配置 + 当前 BASE_URL
|
|
5
|
+
*/
|
|
6
|
+
export declare function detectIDEs(): Promise<GatewayDetection[]>;
|
|
7
|
+
/**
|
|
8
|
+
* 配置一个 IDE 的 gateway BASE_URL (写 config + 提供 revert 命令)
|
|
9
|
+
*
|
|
10
|
+
* 真改: 在每个 config 加 ANTHROPIC_BASE_URL / OPENAI_API_BASE 指向 SkillFM gateway
|
|
11
|
+
* 透明: 改之前 backup 原 config 到 ~/.skillfm/gateway-backup/<ts>/
|
|
12
|
+
*/
|
|
13
|
+
export interface SetupResult {
|
|
14
|
+
ide: string;
|
|
15
|
+
applied: boolean;
|
|
16
|
+
config_path?: string;
|
|
17
|
+
backup_path?: string;
|
|
18
|
+
revert_command?: string;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function setupClaudeCodeGateway(): Promise<SetupResult>;
|
|
22
|
+
export declare function setupShellEnvGateway(): Promise<SetupResult>;
|
|
23
|
+
/**
|
|
24
|
+
* 一键配 — 所有检测到的 IDE
|
|
25
|
+
*/
|
|
26
|
+
export declare function setupAllDetectedGateways(): Promise<SetupResult[]>;
|
|
27
|
+
//# sourceMappingURL=gateway-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-setup.d.ts","sourceRoot":"","sources":["../../src/save-token/gateway-setup.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,YAAY,CAAC;AAEvE,eAAO,MAAM,wBAAwB,QAAuE,CAAC;AAE7G;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAsB9D;AAyFD;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,WAAW,CAAC,CAiCnE;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,WAAW,CAAC,CAoCjE;AAED;;GAEG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAcvE"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// sdk/skillfm-local/src/save-token/gateway-setup.ts
|
|
2
|
+
//
|
|
3
|
+
// LLM Gateway Onboarding 一键配置 (PRD V1.3 §3.4 + 决策 #8).
|
|
4
|
+
// Eric 拍板: Day 0 一次问授权, Pro 升级 0 摩擦.
|
|
5
|
+
//
|
|
6
|
+
// 真核心:
|
|
7
|
+
// 1. detectIDEs() — 看 PATH / 已知配置目录, 检测用户用啥 IDE
|
|
8
|
+
// 2. setupGateway() — 改 IDE config 把 ANTHROPIC_BASE_URL / OPENAI_API_BASE 指向 SkillFM gateway
|
|
9
|
+
// 3. revertGateway() — 1 行 unset 命令让用户随时回滚
|
|
10
|
+
//
|
|
11
|
+
// 透明性: 改任何文件前 surface 给用户 + 提供 revert 命令.
|
|
12
|
+
// MVP: gateway 端 URL 用 placeholder (https://gateway.skillfm.ai), 真 gateway 服务 2.7.5 上线.
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import { homedir } from 'node:os';
|
|
16
|
+
export const SKILLFM_GATEWAY_BASE_URL = process.env.SKILLFM_GATEWAY_BASE_URL ?? 'https://gateway.skillfm.ai';
|
|
17
|
+
/**
|
|
18
|
+
* 检测各 IDE 的配置 + 当前 BASE_URL
|
|
19
|
+
*/
|
|
20
|
+
export async function detectIDEs() {
|
|
21
|
+
const out = [];
|
|
22
|
+
// Claude Code: ~/.claude/settings.json
|
|
23
|
+
out.push(await detectClaudeCode());
|
|
24
|
+
// Cursor: ~/Library/Application Support/Cursor/User/settings.json (Mac)
|
|
25
|
+
out.push(await detectCursor());
|
|
26
|
+
// Codex: ~/.codex/config.json
|
|
27
|
+
out.push(await detectCodex());
|
|
28
|
+
// Aider: env CHECK
|
|
29
|
+
out.push(await detectAider());
|
|
30
|
+
// Continue: ~/.continue/config.json
|
|
31
|
+
out.push(await detectContinue());
|
|
32
|
+
// Shell env (zsh / bash)
|
|
33
|
+
out.push(await detectShellEnv());
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
async function detectClaudeCode() {
|
|
37
|
+
const settingsPath = path.join(homedir(), '.claude', 'settings.json');
|
|
38
|
+
const exists = fs.existsSync(settingsPath);
|
|
39
|
+
let current_base_url;
|
|
40
|
+
let setup_status = 'not_configured';
|
|
41
|
+
if (exists) {
|
|
42
|
+
try {
|
|
43
|
+
const content = await fs.promises.readFile(settingsPath, 'utf-8');
|
|
44
|
+
const settings = JSON.parse(content);
|
|
45
|
+
current_base_url = settings.env?.ANTHROPIC_BASE_URL;
|
|
46
|
+
setup_status = current_base_url?.includes('skillfm') ? 'configured' : 'not_configured';
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
setup_status = 'unknown';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
ide: 'claude-code',
|
|
54
|
+
detected: exists,
|
|
55
|
+
config_path: exists ? settingsPath : undefined,
|
|
56
|
+
current_base_url,
|
|
57
|
+
setup_status,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function detectCursor() {
|
|
61
|
+
const cursorDir = path.join(homedir(), 'Library', 'Application Support', 'Cursor');
|
|
62
|
+
const detected = fs.existsSync(cursorDir);
|
|
63
|
+
return {
|
|
64
|
+
ide: 'cursor',
|
|
65
|
+
detected,
|
|
66
|
+
config_path: detected ? path.join(cursorDir, 'User', 'settings.json') : undefined,
|
|
67
|
+
setup_status: detected ? 'not_configured' : 'unknown',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async function detectCodex() {
|
|
71
|
+
const cfgPath = path.join(homedir(), '.codex', 'config.json');
|
|
72
|
+
const exists = fs.existsSync(cfgPath);
|
|
73
|
+
return {
|
|
74
|
+
ide: 'codex',
|
|
75
|
+
detected: exists,
|
|
76
|
+
config_path: exists ? cfgPath : undefined,
|
|
77
|
+
setup_status: exists ? 'not_configured' : 'unknown',
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function detectAider() {
|
|
81
|
+
// Aider 用 env 配, 没固定 config 文件
|
|
82
|
+
return {
|
|
83
|
+
ide: 'aider',
|
|
84
|
+
detected: false, // 简化: MVP 不主动检测 aider, 用户手动配
|
|
85
|
+
setup_status: 'unknown',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function detectContinue() {
|
|
89
|
+
const cfgPath = path.join(homedir(), '.continue', 'config.json');
|
|
90
|
+
const exists = fs.existsSync(cfgPath);
|
|
91
|
+
return {
|
|
92
|
+
ide: 'continue',
|
|
93
|
+
detected: exists,
|
|
94
|
+
config_path: exists ? cfgPath : undefined,
|
|
95
|
+
setup_status: exists ? 'not_configured' : 'unknown',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async function detectShellEnv() {
|
|
99
|
+
const current_anthropic = process.env.ANTHROPIC_BASE_URL;
|
|
100
|
+
const current_openai = process.env.OPENAI_API_BASE;
|
|
101
|
+
const isConfigured = !!(current_anthropic?.includes('skillfm') || current_openai?.includes('skillfm'));
|
|
102
|
+
// 检测 shell rc
|
|
103
|
+
const zshrc = path.join(homedir(), '.zshrc');
|
|
104
|
+
const bashrc = path.join(homedir(), '.bashrc');
|
|
105
|
+
const cfgPath = fs.existsSync(zshrc) ? zshrc : fs.existsSync(bashrc) ? bashrc : undefined;
|
|
106
|
+
return {
|
|
107
|
+
ide: 'shell-env',
|
|
108
|
+
detected: !!cfgPath,
|
|
109
|
+
config_path: cfgPath,
|
|
110
|
+
current_base_url: current_anthropic ?? current_openai,
|
|
111
|
+
setup_status: isConfigured ? 'configured' : 'not_configured',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export async function setupClaudeCodeGateway() {
|
|
115
|
+
const settingsPath = path.join(homedir(), '.claude', 'settings.json');
|
|
116
|
+
const ide = 'claude-code';
|
|
117
|
+
if (!fs.existsSync(settingsPath)) {
|
|
118
|
+
// Claude Code 不存在 → skip
|
|
119
|
+
return { ide, applied: false, error: 'Claude Code not detected' };
|
|
120
|
+
}
|
|
121
|
+
// backup
|
|
122
|
+
const backupDir = path.join(homedir(), '.skillfm', 'gateway-backup', new Date().toISOString().replace(/[:.]/g, '-'));
|
|
123
|
+
await fs.promises.mkdir(backupDir, { recursive: true, mode: 0o700 });
|
|
124
|
+
const backupPath = path.join(backupDir, 'claude-settings.json');
|
|
125
|
+
await fs.promises.copyFile(settingsPath, backupPath);
|
|
126
|
+
// 改 config
|
|
127
|
+
try {
|
|
128
|
+
const content = await fs.promises.readFile(settingsPath, 'utf-8');
|
|
129
|
+
const settings = JSON.parse(content);
|
|
130
|
+
settings.env = settings.env ?? {};
|
|
131
|
+
settings.env.ANTHROPIC_BASE_URL = SKILLFM_GATEWAY_BASE_URL;
|
|
132
|
+
await fs.promises.writeFile(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
|
|
133
|
+
return {
|
|
134
|
+
ide,
|
|
135
|
+
applied: true,
|
|
136
|
+
config_path: settingsPath,
|
|
137
|
+
backup_path: backupPath,
|
|
138
|
+
revert_command: `cp "${backupPath}" "${settingsPath}"`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
return { ide, applied: false, error: e.message };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export async function setupShellEnvGateway() {
|
|
146
|
+
const ide = 'shell-env';
|
|
147
|
+
const zshrc = path.join(homedir(), '.zshrc');
|
|
148
|
+
const bashrc = path.join(homedir(), '.bashrc');
|
|
149
|
+
const rcPath = fs.existsSync(zshrc) ? zshrc : fs.existsSync(bashrc) ? bashrc : null;
|
|
150
|
+
if (!rcPath) {
|
|
151
|
+
return { ide, applied: false, error: 'No shell rc found' };
|
|
152
|
+
}
|
|
153
|
+
const backupDir = path.join(homedir(), '.skillfm', 'gateway-backup', new Date().toISOString().replace(/[:.]/g, '-'));
|
|
154
|
+
await fs.promises.mkdir(backupDir, { recursive: true, mode: 0o700 });
|
|
155
|
+
const backupPath = path.join(backupDir, path.basename(rcPath));
|
|
156
|
+
await fs.promises.copyFile(rcPath, backupPath);
|
|
157
|
+
const block = [
|
|
158
|
+
'',
|
|
159
|
+
'# === SkillFM Gateway (added by skillfm_setup_gateway) ===',
|
|
160
|
+
`export ANTHROPIC_BASE_URL="${SKILLFM_GATEWAY_BASE_URL}/anthropic"`,
|
|
161
|
+
`export OPENAI_API_BASE="${SKILLFM_GATEWAY_BASE_URL}/openai"`,
|
|
162
|
+
'# === SkillFM Gateway end ===',
|
|
163
|
+
'',
|
|
164
|
+
].join('\n');
|
|
165
|
+
try {
|
|
166
|
+
await fs.promises.appendFile(rcPath, block, 'utf-8');
|
|
167
|
+
return {
|
|
168
|
+
ide,
|
|
169
|
+
applied: true,
|
|
170
|
+
config_path: rcPath,
|
|
171
|
+
backup_path: backupPath,
|
|
172
|
+
revert_command: `cp "${backupPath}" "${rcPath}" && source "${rcPath}"`,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
return { ide, applied: false, error: e.message };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* 一键配 — 所有检测到的 IDE
|
|
181
|
+
*/
|
|
182
|
+
export async function setupAllDetectedGateways() {
|
|
183
|
+
const detections = await detectIDEs();
|
|
184
|
+
const results = [];
|
|
185
|
+
for (const d of detections) {
|
|
186
|
+
if (!d.detected)
|
|
187
|
+
continue;
|
|
188
|
+
if (d.setup_status === 'configured') {
|
|
189
|
+
results.push({ ide: d.ide, applied: false, error: 'already configured' });
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (d.ide === 'claude-code')
|
|
193
|
+
results.push(await setupClaudeCodeGateway());
|
|
194
|
+
else if (d.ide === 'shell-env')
|
|
195
|
+
results.push(await setupShellEnvGateway());
|
|
196
|
+
// Cursor / Codex / Aider / Continue: MVP 仅 surface 提示用户手动配 (UI 复杂)
|
|
197
|
+
}
|
|
198
|
+
return results;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=gateway-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-setup.js","sourceRoot":"","sources":["../../src/save-token/gateway-setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type { EngineId, FixMethod, SaveTokenSuggestion, SaveTokenScanOptions, GatewaySetupStatus, GatewayDetection, } from './types.js';
|
|
2
|
+
export { scanE1Router } from './e1-router.js';
|
|
3
|
+
export { scanE2Cache } from './e2-cache.js';
|
|
4
|
+
export { scanE3Batch } from './e3-batch.js';
|
|
5
|
+
export { detectIDEs, setupClaudeCodeGateway, setupShellEnvGateway, setupAllDetectedGateways, SKILLFM_GATEWAY_BASE_URL, } from './gateway-setup.js';
|
|
6
|
+
export type { SetupResult } from './gateway-setup.js';
|
|
7
|
+
import type { LocalUsageStore } from '../usage-local/store.js';
|
|
8
|
+
import type { SaveTokenSuggestion, SaveTokenScanOptions } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* 跑全 engine 扫描 (E1+E2+E3 并行) — 主入口用
|
|
11
|
+
*/
|
|
12
|
+
export declare function scanAllEngines(store: LocalUsageStore, opts: SaveTokenScanOptions): Promise<SaveTokenSuggestion[]>;
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/save-token/index.ts"],"names":[],"mappings":"AAKA,YAAY,EACV,QAAQ,EACR,SAAS,EACT,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EACL,UAAU,EACV,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAK5E;;GAEG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAOhC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// sdk/skillfm-local/src/save-token/index.ts
|
|
2
|
+
//
|
|
3
|
+
// Save Token Engine — public API.
|
|
4
|
+
// 真"省 token" 5 大引擎 (PRD V1.3 §6).
|
|
5
|
+
export { scanE1Router } from './e1-router.js';
|
|
6
|
+
export { scanE2Cache } from './e2-cache.js';
|
|
7
|
+
export { scanE3Batch } from './e3-batch.js';
|
|
8
|
+
export { detectIDEs, setupClaudeCodeGateway, setupShellEnvGateway, setupAllDetectedGateways, SKILLFM_GATEWAY_BASE_URL, } from './gateway-setup.js';
|
|
9
|
+
import { scanE1Router } from './e1-router.js';
|
|
10
|
+
import { scanE2Cache } from './e2-cache.js';
|
|
11
|
+
import { scanE3Batch } from './e3-batch.js';
|
|
12
|
+
/**
|
|
13
|
+
* 跑全 engine 扫描 (E1+E2+E3 并行) — 主入口用
|
|
14
|
+
*/
|
|
15
|
+
export async function scanAllEngines(store, opts) {
|
|
16
|
+
const [e1, e2, e3] = await Promise.all([
|
|
17
|
+
scanE1Router(store, opts),
|
|
18
|
+
scanE2Cache(store, opts),
|
|
19
|
+
scanE3Batch(store, opts),
|
|
20
|
+
]);
|
|
21
|
+
return [...e1, ...e2, ...e3].sort((a, b) => b.estimated_savings_cny - a.estimated_savings_cny);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/save-token/index.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,kCAAkC;AAClC,kCAAkC;AAUlC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EACL,UAAU,EACV,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAsB,EACtB,IAA0B;IAE1B,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACrC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC;QACzB,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC;QACxB,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC;KACzB,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,qBAAqB,GAAG,CAAC,CAAC,qBAAqB,CAAC,CAAC;AACjG,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type EngineId = 'E1_router' | 'E2_cache' | 'E3_batch' | 'E4_compress' | 'E5_rotate';
|
|
2
|
+
export type FixMethod = 'free_manual' | 'pro_auto' | 'pro_locked';
|
|
3
|
+
/**
|
|
4
|
+
* 一条省 token 建议 (来自任一 engine 的扫描)
|
|
5
|
+
*/
|
|
6
|
+
export interface SaveTokenSuggestion {
|
|
7
|
+
engine: EngineId;
|
|
8
|
+
/** 短文案 — surface 给 agent / 用户 (e.g. "5 次 Sonnet 可切 Haiku") */
|
|
9
|
+
headline: string;
|
|
10
|
+
/** 估算省 token 百分比 (0-100) */
|
|
11
|
+
estimated_savings_pct: number;
|
|
12
|
+
/** 估算月省 token (基于过去 7 天日均) */
|
|
13
|
+
estimated_savings_tokens: number;
|
|
14
|
+
/** 估算月省 CNY (基于价目表) */
|
|
15
|
+
estimated_savings_cny: number;
|
|
16
|
+
/** 修法 */
|
|
17
|
+
fix_method: FixMethod;
|
|
18
|
+
/** Free 用户手动操作步骤 */
|
|
19
|
+
fix_instructions?: string;
|
|
20
|
+
/** Pro 升级文案 */
|
|
21
|
+
pro_unlock_label?: string;
|
|
22
|
+
/** Engine 详情 — 比如哪个 model / 哪个 prompt 触发了建议 */
|
|
23
|
+
details?: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Save Token Engine 扫描请求
|
|
27
|
+
*/
|
|
28
|
+
export interface SaveTokenScanOptions {
|
|
29
|
+
/** 是否 Pro 用户 */
|
|
30
|
+
is_pro: boolean;
|
|
31
|
+
/** 时间窗口 (默认近 7 天) */
|
|
32
|
+
start?: string;
|
|
33
|
+
end?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Gateway setup 状态 (per IDE)
|
|
37
|
+
*/
|
|
38
|
+
export type GatewaySetupStatus = 'not_configured' | 'configured' | 'partial' | 'unknown';
|
|
39
|
+
export interface GatewayDetection {
|
|
40
|
+
ide: 'claude-code' | 'cursor' | 'codex' | 'aider' | 'continue' | 'shell-env';
|
|
41
|
+
detected: boolean;
|
|
42
|
+
config_path?: string;
|
|
43
|
+
current_base_url?: string;
|
|
44
|
+
setup_status: GatewaySetupStatus;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/save-token/types.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAChB,WAAW,GACX,UAAU,GACV,UAAU,GACV,aAAa,GACb,WAAW,CAAC;AAEhB,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,YAAY,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,CAAC;IACjB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,8BAA8B;IAC9B,wBAAwB,EAAE,MAAM,CAAC;IACjC,uBAAuB;IACvB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS;IACT,UAAU,EAAE,SAAS,CAAC;IACtB,oBAAoB;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,gBAAgB;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,gBAAgB,GAChB,YAAY,GACZ,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,CAAC;IAC7E,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,kBAAkB,CAAC;CAClC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/save-token/types.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,+CAA+C"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { OpenClawWatcher } from './openclaw-watcher.js';
|
|
2
|
+
import { LocalUsageStore } from './store.js';
|
|
3
|
+
export type { LocalUsageRecord, AgentHarness, UsageAggregation, UsageByProvider, UsageByAgent, UsageByModel, WatcherConfig, WatchPath, } from './types.js';
|
|
4
|
+
export { LocalUsageStore, timeWindow } from './store.js';
|
|
5
|
+
export type { UsageQuery } from './store.js';
|
|
6
|
+
export { OpenClawWatcher } from './openclaw-watcher.js';
|
|
7
|
+
export { getModelPrice, estimateCostUsd, estimateCostCny, listVerifiedModels, USD_TO_CNY, PRICING_TABLE } from './model-pricing.js';
|
|
8
|
+
export type { ModelPrice } from './model-pricing.js';
|
|
9
|
+
export interface UsageLocalDeps {
|
|
10
|
+
/** Override store path (默认 ~/.skillfm/usage-local.jsonl) */
|
|
11
|
+
storePath?: string;
|
|
12
|
+
/** Override OpenClaw agents dir (默认 ~/.openclaw/agents) */
|
|
13
|
+
openclawAgentsDir?: string;
|
|
14
|
+
/** verbose 日志到 stderr */
|
|
15
|
+
verbose?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface UsageLocalHandle {
|
|
18
|
+
store: LocalUsageStore;
|
|
19
|
+
openclawWatcher: OpenClawWatcher;
|
|
20
|
+
/** 主动 scan 一次所有 trajectory (cron poll fallback 或手动 trigger) */
|
|
21
|
+
scanAll: () => Promise<number>;
|
|
22
|
+
/** 关闭所有 watcher */
|
|
23
|
+
stop: () => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 启动 Usage Local 子系统:
|
|
27
|
+
* 1. load store 从 jsonl
|
|
28
|
+
* 2. 启动 OpenClaw watcher (warm scan + fs.watch)
|
|
29
|
+
* 3. 返回 handle 供 sidecar / scheduler 用
|
|
30
|
+
*/
|
|
31
|
+
export declare function startUsageLocal(deps?: UsageLocalDeps): Promise<UsageLocalHandle>;
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/usage-local/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,YAAY,EACV,gBAAgB,EAAE,YAAY,EAAE,gBAAgB,EAChD,eAAe,EAAE,YAAY,EAAE,YAAY,EAC3C,aAAa,EAAE,SAAS,GACzB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACpI,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yBAAyB;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,eAAe,CAAC;IACvB,eAAe,EAAE,eAAe,CAAC;IACjC,+DAA+D;IAC/D,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B,mBAAmB;IACnB,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiB1F"}
|