@skillfm/local 2.7.3 → 2.7.5

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 (120) hide show
  1. package/dist/checkup/daily-push.d.ts +29 -0
  2. package/dist/checkup/daily-push.d.ts.map +1 -0
  3. package/dist/checkup/daily-push.js +86 -0
  4. package/dist/checkup/daily-push.js.map +1 -0
  5. package/dist/checkup/dimension-1-usage.d.ts +22 -0
  6. package/dist/checkup/dimension-1-usage.d.ts.map +1 -0
  7. package/dist/checkup/dimension-1-usage.js +115 -0
  8. package/dist/checkup/dimension-1-usage.js.map +1 -0
  9. package/dist/checkup/dimension-3-files.d.ts +36 -0
  10. package/dist/checkup/dimension-3-files.d.ts.map +1 -0
  11. package/dist/checkup/dimension-3-files.js +276 -0
  12. package/dist/checkup/dimension-3-files.js.map +1 -0
  13. package/dist/checkup/dimension-4-context.d.ts +18 -0
  14. package/dist/checkup/dimension-4-context.d.ts.map +1 -0
  15. package/dist/checkup/dimension-4-context.js +177 -0
  16. package/dist/checkup/dimension-4-context.js.map +1 -0
  17. package/dist/checkup/index.d.ts +11 -0
  18. package/dist/checkup/index.d.ts.map +1 -0
  19. package/dist/checkup/index.js +10 -0
  20. package/dist/checkup/index.js.map +1 -0
  21. package/dist/checkup/report.d.ts +26 -0
  22. package/dist/checkup/report.d.ts.map +1 -0
  23. package/dist/checkup/report.js +173 -0
  24. package/dist/checkup/report.js.map +1 -0
  25. package/dist/checkup/score.d.ts +27 -0
  26. package/dist/checkup/score.d.ts.map +1 -0
  27. package/dist/checkup/score.js +101 -0
  28. package/dist/checkup/score.js.map +1 -0
  29. package/dist/checkup/types.d.ts +64 -0
  30. package/dist/checkup/types.d.ts.map +1 -0
  31. package/dist/checkup/types.js +5 -0
  32. package/dist/checkup/types.js.map +1 -0
  33. package/dist/mcp/index.d.ts.map +1 -1
  34. package/dist/mcp/index.js +27 -0
  35. package/dist/mcp/index.js.map +1 -1
  36. package/dist/mcp/tools/checkup.d.ts +21 -0
  37. package/dist/mcp/tools/checkup.d.ts.map +1 -0
  38. package/dist/mcp/tools/checkup.js +35 -0
  39. package/dist/mcp/tools/checkup.js.map +1 -0
  40. package/dist/mcp/tools/index.d.ts +6 -0
  41. package/dist/mcp/tools/index.d.ts.map +1 -1
  42. package/dist/mcp/tools/index.js +47 -0
  43. package/dist/mcp/tools/index.js.map +1 -1
  44. package/dist/mcp/tools/setup-gateway.d.ts +16 -0
  45. package/dist/mcp/tools/setup-gateway.d.ts.map +1 -0
  46. package/dist/mcp/tools/setup-gateway.js +79 -0
  47. package/dist/mcp/tools/setup-gateway.js.map +1 -0
  48. package/dist/mcp/tools/show-my-usage.d.ts +26 -0
  49. package/dist/mcp/tools/show-my-usage.d.ts.map +1 -0
  50. package/dist/mcp/tools/show-my-usage.js +68 -0
  51. package/dist/mcp/tools/show-my-usage.js.map +1 -0
  52. package/dist/reconciliation/engine.d.ts +30 -0
  53. package/dist/reconciliation/engine.d.ts.map +1 -0
  54. package/dist/reconciliation/engine.js +62 -0
  55. package/dist/reconciliation/engine.js.map +1 -0
  56. package/dist/reconciliation/index.d.ts +8 -0
  57. package/dist/reconciliation/index.d.ts.map +1 -0
  58. package/dist/reconciliation/index.js +8 -0
  59. package/dist/reconciliation/index.js.map +1 -0
  60. package/dist/reconciliation/l1-reconciler.d.ts +29 -0
  61. package/dist/reconciliation/l1-reconciler.d.ts.map +1 -0
  62. package/dist/reconciliation/l1-reconciler.js +144 -0
  63. package/dist/reconciliation/l1-reconciler.js.map +1 -0
  64. package/dist/reconciliation/l2-reconciler.d.ts +21 -0
  65. package/dist/reconciliation/l2-reconciler.d.ts.map +1 -0
  66. package/dist/reconciliation/l2-reconciler.js +241 -0
  67. package/dist/reconciliation/l2-reconciler.js.map +1 -0
  68. package/dist/reconciliation/types.d.ts +49 -0
  69. package/dist/reconciliation/types.d.ts.map +1 -0
  70. package/dist/reconciliation/types.js +9 -0
  71. package/dist/reconciliation/types.js.map +1 -0
  72. package/dist/save-token/e1-router.d.ts +7 -0
  73. package/dist/save-token/e1-router.d.ts.map +1 -0
  74. package/dist/save-token/e1-router.js +112 -0
  75. package/dist/save-token/e1-router.js.map +1 -0
  76. package/dist/save-token/e2-cache.d.ts +7 -0
  77. package/dist/save-token/e2-cache.d.ts.map +1 -0
  78. package/dist/save-token/e2-cache.js +128 -0
  79. package/dist/save-token/e2-cache.js.map +1 -0
  80. package/dist/save-token/e3-batch.d.ts +7 -0
  81. package/dist/save-token/e3-batch.d.ts.map +1 -0
  82. package/dist/save-token/e3-batch.js +80 -0
  83. package/dist/save-token/e3-batch.js.map +1 -0
  84. package/dist/save-token/gateway-setup.d.ts +27 -0
  85. package/dist/save-token/gateway-setup.d.ts.map +1 -0
  86. package/dist/save-token/gateway-setup.js +200 -0
  87. package/dist/save-token/gateway-setup.js.map +1 -0
  88. package/dist/save-token/index.d.ts +13 -0
  89. package/dist/save-token/index.d.ts.map +1 -0
  90. package/dist/save-token/index.js +23 -0
  91. package/dist/save-token/index.js.map +1 -0
  92. package/dist/save-token/types.d.ts +46 -0
  93. package/dist/save-token/types.d.ts.map +1 -0
  94. package/dist/save-token/types.js +5 -0
  95. package/dist/save-token/types.js.map +1 -0
  96. package/dist/usage-local/index.d.ts +34 -0
  97. package/dist/usage-local/index.d.ts.map +1 -0
  98. package/dist/usage-local/index.js +33 -0
  99. package/dist/usage-local/index.js.map +1 -0
  100. package/dist/usage-local/model-pricing.d.ts +42 -0
  101. package/dist/usage-local/model-pricing.d.ts.map +1 -0
  102. package/dist/usage-local/model-pricing.js +253 -0
  103. package/dist/usage-local/model-pricing.js.map +1 -0
  104. package/dist/usage-local/openclaw-watcher.d.ts +35 -0
  105. package/dist/usage-local/openclaw-watcher.d.ts.map +1 -0
  106. package/dist/usage-local/openclaw-watcher.js +190 -0
  107. package/dist/usage-local/openclaw-watcher.js.map +1 -0
  108. package/dist/usage-local/plan.d.ts +58 -0
  109. package/dist/usage-local/plan.d.ts.map +1 -0
  110. package/dist/usage-local/plan.js +84 -0
  111. package/dist/usage-local/plan.js.map +1 -0
  112. package/dist/usage-local/store.d.ts +50 -0
  113. package/dist/usage-local/store.d.ts.map +1 -0
  114. package/dist/usage-local/store.js +201 -0
  115. package/dist/usage-local/store.js.map +1 -0
  116. package/dist/usage-local/types.d.ts +94 -0
  117. package/dist/usage-local/types.d.ts.map +1 -0
  118. package/dist/usage-local/types.js +6 -0
  119. package/dist/usage-local/types.js.map +1 -0
  120. package/package.json +2 -2
@@ -0,0 +1,190 @@
1
+ // sdk/skillfm-local/src/usage-local/openclaw-watcher.ts
2
+ //
3
+ // OpenClaw trajectory watcher — 监听 ~/.openclaw/agents/<id>/sessions/*.trajectory.jsonl
4
+ // 提 model.completed events 转 LocalUsageRecord, 调 store.appendBatch().
5
+ //
6
+ // Schema 真验证 (2026-05-03 在赵云 OpenClaw 2026.4.29 上):
7
+ // model.completed event 自带 {ts, provider, modelId, data: {usage: {input, output, cacheRead, cacheWrite, total}}}
8
+ //
9
+ // 设计: 用 node 内置 fs.watch (不引入 chokidar). 增量读 (记录每个 file 的最后 byte offset).
10
+ import * as fs from 'node:fs';
11
+ import * as path from 'node:path';
12
+ import { homedir } from 'node:os';
13
+ import { estimateCostUsd, estimateCostCny } from './model-pricing.js';
14
+ const DEFAULT_OPENCLAW_AGENTS_DIR = path.join(homedir(), '.openclaw', 'agents');
15
+ /**
16
+ * OpenClaw trajectory watcher
17
+ *
18
+ * 启动后:
19
+ * 1. 扫现有 *.trajectory.jsonl, 全量解析 (warm start)
20
+ * 2. fs.watch 监听新文件 + 现有文件 append
21
+ * 3. 增量解析 (记录每个 file 的最后 byte offset)
22
+ * 4. 调 store.appendBatch 持久化
23
+ */
24
+ export class OpenClawWatcher {
25
+ offsets = new Map(); // file path → last byte offset
26
+ watchers = [];
27
+ agentsDir;
28
+ agent_harness = 'openclaw';
29
+ store;
30
+ verbose;
31
+ constructor(opts) {
32
+ this.store = opts.store;
33
+ this.agentsDir = opts.agentsDir ?? DEFAULT_OPENCLAW_AGENTS_DIR;
34
+ this.verbose = opts.verbose ?? false;
35
+ }
36
+ /** 启动:warm-scan 现有文件 + 注册 fs.watch */
37
+ async start() {
38
+ if (!fs.existsSync(this.agentsDir)) {
39
+ this.log(`OpenClaw agents dir not found: ${this.agentsDir} — skipping watcher`);
40
+ return;
41
+ }
42
+ // Warm scan — 解析所有现有 trajectory.jsonl
43
+ const files = await this.discoverTrajectoryFiles();
44
+ this.log(`warm-scanning ${files.length} OpenClaw trajectory files`);
45
+ for (const file of files) {
46
+ await this.scanFile(file);
47
+ }
48
+ // 注册 fs.watch — 监听 agents/ 目录递归
49
+ this.registerWatchers();
50
+ this.log('OpenClaw watcher live');
51
+ }
52
+ /** 扫一次现有所有文件 (manual trigger, 用于 cron poll fallback) */
53
+ async scanAll() {
54
+ const files = await this.discoverTrajectoryFiles();
55
+ let total = 0;
56
+ for (const file of files) {
57
+ total += await this.scanFile(file);
58
+ }
59
+ return total;
60
+ }
61
+ /** 关闭 watcher */
62
+ stop() {
63
+ for (const w of this.watchers)
64
+ w.close();
65
+ this.watchers = [];
66
+ }
67
+ // ─────────────────────────────────────────────────────────────────
68
+ async discoverTrajectoryFiles() {
69
+ const out = [];
70
+ const agents = await fs.promises.readdir(this.agentsDir, { withFileTypes: true });
71
+ for (const agent of agents) {
72
+ if (!agent.isDirectory())
73
+ continue;
74
+ const sessionsDir = path.join(this.agentsDir, agent.name, 'sessions');
75
+ if (!fs.existsSync(sessionsDir))
76
+ continue;
77
+ const sessions = await fs.promises.readdir(sessionsDir);
78
+ for (const f of sessions) {
79
+ if (f.endsWith('.trajectory.jsonl')) {
80
+ out.push(path.join(sessionsDir, f));
81
+ }
82
+ }
83
+ }
84
+ return out;
85
+ }
86
+ async scanFile(filePath) {
87
+ const stat = await fs.promises.stat(filePath).catch(() => null);
88
+ if (!stat || stat.size === 0)
89
+ return 0;
90
+ const lastOffset = this.offsets.get(filePath) ?? 0;
91
+ if (stat.size <= lastOffset)
92
+ return 0; // no new data
93
+ // 增量读: 从 lastOffset 到 size
94
+ const buf = Buffer.alloc(stat.size - lastOffset);
95
+ const fd = await fs.promises.open(filePath, 'r');
96
+ try {
97
+ await fd.read(buf, 0, buf.length, lastOffset);
98
+ }
99
+ finally {
100
+ await fd.close();
101
+ }
102
+ const text = buf.toString('utf-8');
103
+ const records = [];
104
+ let validLineEnd = 0; // 记录最后一个完整行的位置 (jsonl 可能 truncated)
105
+ let cursor = 0;
106
+ while (cursor < text.length) {
107
+ const newlineIdx = text.indexOf('\n', cursor);
108
+ if (newlineIdx === -1)
109
+ break; // 不完整行,等下次 scan
110
+ const line = text.slice(cursor, newlineIdx).trim();
111
+ cursor = newlineIdx + 1;
112
+ validLineEnd = lastOffset + cursor;
113
+ if (!line || !line.startsWith('{'))
114
+ continue;
115
+ try {
116
+ const event = JSON.parse(line);
117
+ const record = this.eventToRecord(event, filePath);
118
+ if (record)
119
+ records.push(record);
120
+ }
121
+ catch {
122
+ // 解析失败 (jsonl 偶尔 partial), 跳过
123
+ }
124
+ }
125
+ // 更新 offset (只 update 到最后完整行,partial 行下次再读)
126
+ this.offsets.set(filePath, validLineEnd);
127
+ if (records.length > 0) {
128
+ await this.store.appendBatch(records);
129
+ this.log(`${path.basename(filePath)}: +${records.length} records`);
130
+ }
131
+ return records.length;
132
+ }
133
+ eventToRecord(event, filePath) {
134
+ if (event.type !== 'model.completed')
135
+ return null;
136
+ const usage = event.data?.usage;
137
+ if (!usage)
138
+ return null;
139
+ const provider = event.provider ?? 'unknown';
140
+ const model_id = event.modelId ?? 'unknown';
141
+ const input_tokens = usage.input ?? 0;
142
+ const output_tokens = usage.output ?? 0;
143
+ const cache_read_tokens = usage.cacheRead ?? 0;
144
+ const cache_write_tokens = usage.cacheWrite ?? 0;
145
+ const total_tokens = (usage.total ?? input_tokens + output_tokens);
146
+ const id = `oc:${event.sessionId}:${event.ts}:${event.runId ?? ''}`;
147
+ return {
148
+ id,
149
+ timestamp: event.ts,
150
+ agent_harness: this.agent_harness,
151
+ session_id: event.sessionId,
152
+ provider,
153
+ model_id,
154
+ input_tokens,
155
+ output_tokens,
156
+ cache_read_tokens,
157
+ cache_write_tokens,
158
+ total_tokens,
159
+ estimated_cost_usd: estimateCostUsd(provider, model_id, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens),
160
+ estimated_cost_cny: estimateCostCny(provider, model_id, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens),
161
+ source_file: filePath,
162
+ };
163
+ }
164
+ registerWatchers() {
165
+ // 单层 watch agents/ + 子目录变化时再 walk
166
+ try {
167
+ const w = fs.watch(this.agentsDir, { recursive: true }, (_eventType, filename) => {
168
+ if (!filename)
169
+ return;
170
+ if (!filename.endsWith('.trajectory.jsonl'))
171
+ return;
172
+ const fullPath = path.join(this.agentsDir, filename);
173
+ // 异步 scan, 不 block watcher
174
+ this.scanFile(fullPath).catch((e) => {
175
+ this.log(`scan failed ${filename}: ${e.message}`);
176
+ });
177
+ });
178
+ this.watchers.push(w);
179
+ }
180
+ catch (e) {
181
+ // 某些系统不支持 recursive (Linux 部分内核), fallback 不报错
182
+ this.log(`fs.watch recursive not supported, falling back to cron-only: ${e.message}`);
183
+ }
184
+ }
185
+ log(msg) {
186
+ if (this.verbose)
187
+ console.error(`[usage-local/openclaw] ${msg}`);
188
+ }
189
+ }
190
+ //# sourceMappingURL=openclaw-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openclaw-watcher.js","sourceRoot":"","sources":["../../src/usage-local/openclaw-watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ export type PlanType = 'pay-per-token' | 'subscription' | 'mixed';
2
+ export interface ProviderPlan {
3
+ /** Provider 标识 (e.g. 'minimax-cn', 'openai', 'anthropic') */
4
+ provider: string;
5
+ /** Plan 类型 */
6
+ type: PlanType;
7
+ /** 订阅相关 (type='subscription' 时必填) */
8
+ subscription?: {
9
+ /** 月度 token 额度 (input + output) */
10
+ tokens_quota_monthly: number;
11
+ /** 当月起始日期 (ISO date) — 通常是订阅日 */
12
+ period_start_iso: string;
13
+ /** Plan 显示名 (e.g. "Claude Pro / Cursor Pro / MiniMax 月度套餐") */
14
+ display_name: string;
15
+ /** 月费 (用于"性价比" 对比) */
16
+ monthly_price_cny?: number;
17
+ };
18
+ }
19
+ export interface PlanConfig {
20
+ /** Default plan type (没显式配的 provider 走这个) */
21
+ default_type: PlanType;
22
+ /** Per-provider 配置 */
23
+ providers: Record<string, ProviderPlan>;
24
+ }
25
+ export declare const DEFAULT_PLAN_CONFIG: PlanConfig;
26
+ /**
27
+ * 加载 plan 配置, 没有则返默认
28
+ */
29
+ export declare function loadPlanConfig(filePath?: string): Promise<PlanConfig>;
30
+ /**
31
+ * 保存 plan 配置
32
+ */
33
+ export declare function savePlanConfig(cfg: PlanConfig, filePath?: string): Promise<void>;
34
+ /**
35
+ * 查 provider 真 plan (没显式配则返 default 类型 + 空 subscription)
36
+ */
37
+ export declare function getProviderPlan(cfg: PlanConfig, provider: string): ProviderPlan;
38
+ /**
39
+ * 渲染 provider 用量行 — 根据 plan 类型自动切换 surface 模式
40
+ *
41
+ * 输入: provider name + 用量数字
42
+ * 输出:
43
+ * pay-per-token: "minimax-cn: 今日 ¥6.60"
44
+ * subscription: "minimax-cn: 今日 271K token / 套餐 5.4% (Pro 月度)"
45
+ */
46
+ export declare function renderProviderUsage(opts: {
47
+ plan: ProviderPlan;
48
+ total_tokens: number;
49
+ cost_cny: number;
50
+ period: 'today' | 'week' | 'month';
51
+ /** 距月底剩多少 token (subscription 用, 算 days_to_zero) */
52
+ remaining_in_period_tokens?: number;
53
+ }): string;
54
+ /**
55
+ * Token 数格式化 (1234567 → "1.2M", 12345 → "12K")
56
+ */
57
+ export declare function formatTokens(n: number): string;
58
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/usage-local/plan.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,QAAQ,GAChB,eAAe,GACf,cAAc,GACd,OAAO,CAAC;AAEZ,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,qCAAqC;IACrC,YAAY,CAAC,EAAE;QACb,mCAAmC;QACnC,oBAAoB,EAAE,MAAM,CAAC;QAC7B,iCAAiC;QACjC,gBAAgB,EAAE,MAAM,CAAC;QACzB,+DAA+D;QAC/D,YAAY,EAAE,MAAM,CAAC;QACrB,sBAAsB;QACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;CACH;AAED,MAAM,WAAW,UAAU;IACzB,6CAA6C;IAC7C,YAAY,EAAE,QAAQ,CAAC;IACvB,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACzC;AAED,eAAO,MAAM,mBAAmB,EAAE,UAGjC,CAAC;AAEF;;GAEG;AACH,wBAAsB,cAAc,CAAC,QAAQ,GAAE,MAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAStF;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,GAAE,MAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMjG;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAI/E;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,IAAI,EAAE,YAAY,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACnC,oDAAoD;IACpD,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC,GAAG,MAAM,CAgBT;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI9C"}
@@ -0,0 +1,84 @@
1
+ // sdk/skillfm-local/src/usage-local/plan.ts
2
+ //
3
+ // 订阅 Plan 配置 (V1.4 校准 — Eric Q1: 订阅套餐用户不该 surface "¥X 花了").
4
+ //
5
+ // 真问题: Eric 用 MiniMax 是订阅套餐, 我 surface "今日 ¥6.60" 对他没意义.
6
+ // 真该 surface: "今日 271K token / 套餐月度 5M token, 占 5.4%, 30 天耗尽预测".
7
+ //
8
+ // 持久化: ~/.skillfm/plan.json (用户首次配置后写)
9
+ import * as fs from 'node:fs';
10
+ import * as path from 'node:path';
11
+ import { homedir } from 'node:os';
12
+ const PLAN_FILE = path.join(homedir(), '.skillfm', 'plan.json');
13
+ export const DEFAULT_PLAN_CONFIG = {
14
+ default_type: 'pay-per-token',
15
+ providers: {},
16
+ };
17
+ /**
18
+ * 加载 plan 配置, 没有则返默认
19
+ */
20
+ export async function loadPlanConfig(filePath = PLAN_FILE) {
21
+ if (!fs.existsSync(filePath))
22
+ return DEFAULT_PLAN_CONFIG;
23
+ try {
24
+ const text = await fs.promises.readFile(filePath, 'utf-8');
25
+ const cfg = JSON.parse(text);
26
+ return { ...DEFAULT_PLAN_CONFIG, ...cfg };
27
+ }
28
+ catch {
29
+ return DEFAULT_PLAN_CONFIG;
30
+ }
31
+ }
32
+ /**
33
+ * 保存 plan 配置
34
+ */
35
+ export async function savePlanConfig(cfg, filePath = PLAN_FILE) {
36
+ const dir = path.dirname(filePath);
37
+ if (!fs.existsSync(dir)) {
38
+ await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });
39
+ }
40
+ await fs.promises.writeFile(filePath, JSON.stringify(cfg, null, 2), { mode: 0o600 });
41
+ }
42
+ /**
43
+ * 查 provider 真 plan (没显式配则返 default 类型 + 空 subscription)
44
+ */
45
+ export function getProviderPlan(cfg, provider) {
46
+ const exact = cfg.providers[provider.toLowerCase()];
47
+ if (exact)
48
+ return exact;
49
+ return { provider: provider.toLowerCase(), type: cfg.default_type };
50
+ }
51
+ /**
52
+ * 渲染 provider 用量行 — 根据 plan 类型自动切换 surface 模式
53
+ *
54
+ * 输入: provider name + 用量数字
55
+ * 输出:
56
+ * pay-per-token: "minimax-cn: 今日 ¥6.60"
57
+ * subscription: "minimax-cn: 今日 271K token / 套餐 5.4% (Pro 月度)"
58
+ */
59
+ export function renderProviderUsage(opts) {
60
+ const { plan, total_tokens, cost_cny, period } = opts;
61
+ const periodLabel = { today: '今日', week: '本周', month: '本月' }[period];
62
+ if (plan.type === 'subscription' && plan.subscription) {
63
+ const quota = plan.subscription.tokens_quota_monthly;
64
+ const usedPct = quota > 0 ? (total_tokens / quota) * 100 : 0;
65
+ const tokensStr = formatTokens(total_tokens);
66
+ const quotaStr = formatTokens(quota);
67
+ const planName = plan.subscription.display_name;
68
+ return `${plan.provider}: ${periodLabel} ${tokensStr} / 套餐 ${quotaStr} (${usedPct.toFixed(1)}% 已用, ${planName})`;
69
+ }
70
+ // pay-per-token (or unconfigured)
71
+ const tokensStr = formatTokens(total_tokens);
72
+ return `${plan.provider}: ${periodLabel} ${tokensStr} (估 ¥${cost_cny.toFixed(2)})`;
73
+ }
74
+ /**
75
+ * Token 数格式化 (1234567 → "1.2M", 12345 → "12K")
76
+ */
77
+ export function formatTokens(n) {
78
+ if (n >= 1_000_000)
79
+ return (n / 1_000_000).toFixed(1) + 'M';
80
+ if (n >= 1_000)
81
+ return (n / 1_000).toFixed(0) + 'K';
82
+ return String(n);
83
+ }
84
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/usage-local/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import type { LocalUsageRecord, AgentHarness, UsageAggregation, UsageByProvider, UsageByAgent, UsageByModel } from './types.js';
2
+ export interface UsageQuery {
3
+ /** ISO 8601 起始 (含) */
4
+ start?: string;
5
+ /** ISO 8601 结束 (不含) */
6
+ end?: string;
7
+ /** 仅这些 provider */
8
+ providers?: string[];
9
+ /** 仅这些 agent harness */
10
+ agents?: AgentHarness[];
11
+ }
12
+ /**
13
+ * Local usage store
14
+ * - in-memory Map (id → record) for dedup + 快速 query
15
+ * - jsonl 持久化 (append-only, 每条 record 一行)
16
+ * - load() 从 jsonl 重建 in-memory state
17
+ */
18
+ export declare class LocalUsageStore {
19
+ private records;
20
+ private filePath;
21
+ private dirty;
22
+ constructor(opts?: {
23
+ filePath?: string;
24
+ });
25
+ /** 启动时从文件 load */
26
+ load(): Promise<void>;
27
+ /** 批量 append (dedup by id), 写文件 */
28
+ appendBatch(batch: LocalUsageRecord[]): Promise<number>;
29
+ /** 当前所有 records 数 */
30
+ size(): number;
31
+ /** Query records (基础筛选) */
32
+ query(q?: UsageQuery): LocalUsageRecord[];
33
+ /** 时间窗口聚合 (总数) */
34
+ aggregate(q?: UsageQuery): UsageAggregation;
35
+ /** 按 provider 聚合 */
36
+ aggregateByProvider(q?: UsageQuery): UsageByProvider[];
37
+ /** 按 agent harness 聚合 */
38
+ aggregateByAgent(q?: UsageQuery): UsageByAgent[];
39
+ /** 按 (provider, model) 聚合 */
40
+ aggregateByModel(q?: UsageQuery): UsageByModel[];
41
+ /** Helper: 聚合 records 列表 */
42
+ private aggregateRecords;
43
+ /** 清空 (测试用 / 用户主动清) */
44
+ clear(): Promise<void>;
45
+ }
46
+ /**
47
+ * 时间窗口快捷:今日 / 本周 / 本月
48
+ */
49
+ export declare function timeWindow(period: 'today' | 'week' | 'month' | 'all'): UsageQuery;
50
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/usage-local/store.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,EACb,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,UAAU;IACzB,sBAAsB;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,wBAAwB;IACxB,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;CACzB;AAED;;;;;GAKG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,KAAK,CAAS;gBAEV,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO;IAI5C,kBAAkB;IACZ,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB3B,mCAAmC;IAC7B,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAsB7D,qBAAqB;IACrB,IAAI,IAAI,MAAM;IAId,2BAA2B;IAC3B,KAAK,CAAC,CAAC,GAAE,UAAe,GAAG,gBAAgB,EAAE;IAiB7C,kBAAkB;IAClB,SAAS,CAAC,CAAC,GAAE,UAAe,GAAG,gBAAgB;IAK/C,oBAAoB;IACpB,mBAAmB,CAAC,CAAC,GAAE,UAAe,GAAG,eAAe,EAAE;IAe1D,yBAAyB;IACzB,gBAAgB,CAAC,CAAC,GAAE,UAAe,GAAG,YAAY,EAAE;IAcpD,6BAA6B;IAC7B,gBAAgB,CAAC,CAAC,GAAE,UAAe,GAAG,YAAY,EAAE;IAoBpD,4BAA4B;IAC5B,OAAO,CAAC,gBAAgB;IAuBxB,uBAAuB;IACjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAM7B;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU,CAejF"}
@@ -0,0 +1,201 @@
1
+ // sdk/skillfm-local/src/usage-local/store.ts
2
+ //
3
+ // LocalUsageStore — 持久化 + 时序聚合 query.
4
+ // 第一版用 in-memory + JSON 文件持久化 (~/.skillfm/usage-local.json),
5
+ // 0 新依赖. 后续如果数据量大 (>10k records) 再换 sqlite (better-sqlite3).
6
+ //
7
+ // 文件格式: append-only JSONL (每行一个 LocalUsageRecord), 高效 + 可恢复.
8
+ // dedup 靠 record.id (基于 sessionId+timestamp+runId 的稳定 hash).
9
+ import * as fs from 'node:fs';
10
+ import * as path from 'node:path';
11
+ import { homedir } from 'node:os';
12
+ const DEFAULT_STORE_PATH = path.join(homedir(), '.skillfm', 'usage-local.jsonl');
13
+ /**
14
+ * Local usage store
15
+ * - in-memory Map (id → record) for dedup + 快速 query
16
+ * - jsonl 持久化 (append-only, 每条 record 一行)
17
+ * - load() 从 jsonl 重建 in-memory state
18
+ */
19
+ export class LocalUsageStore {
20
+ records = new Map();
21
+ filePath;
22
+ dirty = false;
23
+ constructor(opts = {}) {
24
+ this.filePath = opts.filePath ?? DEFAULT_STORE_PATH;
25
+ }
26
+ /** 启动时从文件 load */
27
+ async load() {
28
+ if (!fs.existsSync(this.filePath)) {
29
+ // 确保目录存在
30
+ const dir = path.dirname(this.filePath);
31
+ if (!fs.existsSync(dir)) {
32
+ await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });
33
+ }
34
+ return;
35
+ }
36
+ const text = await fs.promises.readFile(this.filePath, 'utf-8');
37
+ for (const line of text.split('\n')) {
38
+ const trimmed = line.trim();
39
+ if (!trimmed || !trimmed.startsWith('{'))
40
+ continue;
41
+ try {
42
+ const r = JSON.parse(trimmed);
43
+ if (r.id)
44
+ this.records.set(r.id, r);
45
+ }
46
+ catch {
47
+ // skip malformed
48
+ }
49
+ }
50
+ }
51
+ /** 批量 append (dedup by id), 写文件 */
52
+ async appendBatch(batch) {
53
+ if (batch.length === 0)
54
+ return 0;
55
+ const newRecords = [];
56
+ for (const r of batch) {
57
+ if (!this.records.has(r.id)) {
58
+ this.records.set(r.id, r);
59
+ newRecords.push(r);
60
+ }
61
+ }
62
+ if (newRecords.length === 0)
63
+ return 0;
64
+ // 确保目录存在 (defensive)
65
+ const dir = path.dirname(this.filePath);
66
+ if (!fs.existsSync(dir)) {
67
+ await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });
68
+ }
69
+ const lines = newRecords.map((r) => JSON.stringify(r)).join('\n') + '\n';
70
+ await fs.promises.appendFile(this.filePath, lines, { mode: 0o600 });
71
+ return newRecords.length;
72
+ }
73
+ /** 当前所有 records 数 */
74
+ size() {
75
+ return this.records.size;
76
+ }
77
+ /** Query records (基础筛选) */
78
+ query(q = {}) {
79
+ const startMs = q.start ? Date.parse(q.start) : 0;
80
+ const endMs = q.end ? Date.parse(q.end) : Number.MAX_SAFE_INTEGER;
81
+ const providerSet = q.providers ? new Set(q.providers.map((p) => p.toLowerCase())) : null;
82
+ const agentSet = q.agents ? new Set(q.agents) : null;
83
+ const out = [];
84
+ for (const r of this.records.values()) {
85
+ const ts = Date.parse(r.timestamp);
86
+ if (ts < startMs || ts >= endMs)
87
+ continue;
88
+ if (providerSet && !providerSet.has(r.provider.toLowerCase()))
89
+ continue;
90
+ if (agentSet && !agentSet.has(r.agent_harness))
91
+ continue;
92
+ out.push(r);
93
+ }
94
+ return out.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
95
+ }
96
+ /** 时间窗口聚合 (总数) */
97
+ aggregate(q = {}) {
98
+ const records = this.query(q);
99
+ return this.aggregateRecords(records, q.start ?? '', q.end ?? '');
100
+ }
101
+ /** 按 provider 聚合 */
102
+ aggregateByProvider(q = {}) {
103
+ const records = this.query(q);
104
+ const groups = new Map();
105
+ for (const r of records) {
106
+ const key = r.provider;
107
+ const list = groups.get(key) ?? [];
108
+ list.push(r);
109
+ groups.set(key, list);
110
+ }
111
+ return Array.from(groups.entries()).map(([provider, rs]) => ({
112
+ provider,
113
+ ...this.aggregateRecords(rs, q.start ?? '', q.end ?? ''),
114
+ }));
115
+ }
116
+ /** 按 agent harness 聚合 */
117
+ aggregateByAgent(q = {}) {
118
+ const records = this.query(q);
119
+ const groups = new Map();
120
+ for (const r of records) {
121
+ const list = groups.get(r.agent_harness) ?? [];
122
+ list.push(r);
123
+ groups.set(r.agent_harness, list);
124
+ }
125
+ return Array.from(groups.entries()).map(([agent_harness, rs]) => ({
126
+ agent_harness,
127
+ ...this.aggregateRecords(rs, q.start ?? '', q.end ?? ''),
128
+ }));
129
+ }
130
+ /** 按 (provider, model) 聚合 */
131
+ aggregateByModel(q = {}) {
132
+ const records = this.query(q);
133
+ const groups = new Map();
134
+ for (const r of records) {
135
+ const key = `${r.provider}/${r.model_id}`;
136
+ const list = groups.get(key) ?? [];
137
+ list.push(r);
138
+ groups.set(key, list);
139
+ }
140
+ return Array.from(groups.entries()).map(([key, rs]) => {
141
+ const [provider, ...rest] = key.split('/');
142
+ const model_id = rest.join('/');
143
+ return {
144
+ provider: provider,
145
+ model_id,
146
+ ...this.aggregateRecords(rs, q.start ?? '', q.end ?? ''),
147
+ };
148
+ });
149
+ }
150
+ /** Helper: 聚合 records 列表 */
151
+ aggregateRecords(records, start, end) {
152
+ let input = 0, output = 0, cacheRead = 0, cacheWrite = 0, costUsd = 0, costCny = 0;
153
+ for (const r of records) {
154
+ input += r.input_tokens;
155
+ output += r.output_tokens;
156
+ cacheRead += r.cache_read_tokens;
157
+ cacheWrite += r.cache_write_tokens;
158
+ costUsd += r.estimated_cost_usd;
159
+ costCny += r.estimated_cost_cny;
160
+ }
161
+ return {
162
+ window_start: start,
163
+ window_end: end,
164
+ input_tokens: input,
165
+ output_tokens: output,
166
+ cache_read_tokens: cacheRead,
167
+ cache_write_tokens: cacheWrite,
168
+ total_cost_usd: costUsd,
169
+ total_cost_cny: costCny,
170
+ call_count: records.length,
171
+ };
172
+ }
173
+ /** 清空 (测试用 / 用户主动清) */
174
+ async clear() {
175
+ this.records.clear();
176
+ if (fs.existsSync(this.filePath)) {
177
+ await fs.promises.unlink(this.filePath);
178
+ }
179
+ }
180
+ }
181
+ /**
182
+ * 时间窗口快捷:今日 / 本周 / 本月
183
+ */
184
+ export function timeWindow(period) {
185
+ const now = new Date();
186
+ if (period === 'all')
187
+ return {};
188
+ if (period === 'today') {
189
+ const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
190
+ return { start: start.toISOString() };
191
+ }
192
+ if (period === 'week') {
193
+ const start = new Date(now);
194
+ start.setDate(now.getDate() - 7);
195
+ return { start: start.toISOString() };
196
+ }
197
+ // month
198
+ const start = new Date(now.getFullYear(), now.getMonth(), 1);
199
+ return { start: start.toISOString() };
200
+ }
201
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/usage-local/store.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,6DAA6D;AAC7D,6DAA6D;AAC7D,EAAE;AACF,6DAA6D;AAC7D,6DAA6D;AAE7D,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAWlC,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,mBAAmB,CAAC,CAAC;AAajF;;;;;GAKG;AACH,MAAM,OAAO,eAAe;IAClB,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;IAC9C,QAAQ,CAAS;IACjB,KAAK,GAAG,KAAK,CAAC;IAEtB,YAAY,OAA8B,EAAE;QAC1C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,kBAAkB,CAAC;IACtD,CAAC;IAED,kBAAkB;IAClB,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClC,SAAS;YACT,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACjE,CAAC;YACD,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAChE,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YACnD,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAqB,CAAC;gBAClD,IAAI,CAAC,CAAC,EAAE;oBAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YACtC,CAAC;YAAC,MAAM,CAAC;gBACP,iBAAiB;YACnB,CAAC;QACH,CAAC;IACH,CAAC;IAED,mCAAmC;IACnC,KAAK,CAAC,WAAW,CAAC,KAAyB;QACzC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QACjC,MAAM,UAAU,GAAuB,EAAE,CAAC;QAC1C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC5B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;gBAC1B,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAEtC,qBAAqB;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACzE,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACpE,OAAO,UAAU,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,qBAAqB;IACrB,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;IAED,2BAA2B;IAC3B,KAAK,CAAC,IAAgB,EAAE;QACtB,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAClE,MAAM,WAAW,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1F,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAErD,MAAM,GAAG,GAAuB,EAAE,CAAC;QACnC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YACtC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YACnC,IAAI,EAAE,GAAG,OAAO,IAAI,EAAE,IAAI,KAAK;gBAAE,SAAS;YAC1C,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAAE,SAAS;YACxE,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC;gBAAE,SAAS;YACzD,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACd,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,kBAAkB;IAClB,SAAS,CAAC,IAAgB,EAAE;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,oBAAoB;IACpB,mBAAmB,CAAC,IAAgB,EAAE;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,IAAI,GAAG,EAA8B,CAAC;QACrD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC;YACvB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACb,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3D,QAAQ;YACR,GAAG,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC;SACzD,CAAC,CAAC,CAAC;IACN,CAAC;IAED,yBAAyB;IACzB,gBAAgB,CAAC,IAAgB,EAAE;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoC,CAAC;QAC3D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;YAC/C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACb,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAChE,aAAa;YACb,GAAG,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC;SACzD,CAAC,CAAC,CAAC;IACN,CAAC;IAED,6BAA6B;IAC7B,gBAAgB,CAAC,IAAgB,EAAE;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,IAAI,GAAG,EAA8B,CAAC;QACrD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACb,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;YACpD,MAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChC,OAAO;gBACL,QAAQ,EAAE,QAAS;gBACnB,QAAQ;gBACR,GAAG,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC;aACzD,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,4BAA4B;IACpB,gBAAgB,CAAC,OAA2B,EAAE,KAAa,EAAE,GAAW;QAC9E,IAAI,KAAK,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC,EAAE,UAAU,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC;QACnF,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,KAAK,IAAI,CAAC,CAAC,YAAY,CAAC;YACxB,MAAM,IAAI,CAAC,CAAC,aAAa,CAAC;YAC1B,SAAS,IAAI,CAAC,CAAC,iBAAiB,CAAC;YACjC,UAAU,IAAI,CAAC,CAAC,kBAAkB,CAAC;YACnC,OAAO,IAAI,CAAC,CAAC,kBAAkB,CAAC;YAChC,OAAO,IAAI,CAAC,CAAC,kBAAkB,CAAC;QAClC,CAAC;QACD,OAAO;YACL,YAAY,EAAE,KAAK;YACnB,UAAU,EAAE,GAAG;YACf,YAAY,EAAE,KAAK;YACnB,aAAa,EAAE,MAAM;YACrB,iBAAiB,EAAE,SAAS;YAC5B,kBAAkB,EAAE,UAAU;YAC9B,cAAc,EAAE,OAAO;YACvB,cAAc,EAAE,OAAO;YACvB,UAAU,EAAE,OAAO,CAAC,MAAM;SAC3B,CAAC;IACJ,CAAC;IAED,uBAAuB;IACvB,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,MAA0C;IACnE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,EAAE,CAAC;IAChC,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACzE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;IACxC,CAAC;IACD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5B,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;QACjC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;IACxC,CAAC;IACD,QAAQ;IACR,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;IAC7D,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;AACxC,CAAC"}