@nocoo/pew 0.2.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/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +5 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +221 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/login.d.ts +34 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +108 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/status.d.ts +19 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +35 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +49 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +267 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/upload.d.ts +53 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +185 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/config/manager.d.ts +14 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +32 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/discovery/sources.d.ts +37 -0
- package/dist/discovery/sources.d.ts.map +1 -0
- package/dist/discovery/sources.js +110 -0
- package/dist/discovery/sources.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/claude.d.ts +34 -0
- package/dist/parsers/claude.d.ts.map +1 -0
- package/dist/parsers/claude.js +101 -0
- package/dist/parsers/claude.js.map +1 -0
- package/dist/parsers/gemini.d.ts +38 -0
- package/dist/parsers/gemini.d.ts.map +1 -0
- package/dist/parsers/gemini.js +142 -0
- package/dist/parsers/gemini.js.map +1 -0
- package/dist/parsers/openclaw.d.ts +23 -0
- package/dist/parsers/openclaw.d.ts.map +1 -0
- package/dist/parsers/openclaw.js +85 -0
- package/dist/parsers/openclaw.js.map +1 -0
- package/dist/parsers/opencode.d.ts +35 -0
- package/dist/parsers/opencode.d.ts.map +1 -0
- package/dist/parsers/opencode.js +118 -0
- package/dist/parsers/opencode.js.map +1 -0
- package/dist/storage/cursor-store.d.ts +14 -0
- package/dist/storage/cursor-store.d.ts.map +1 -0
- package/dist/storage/cursor-store.js +34 -0
- package/dist/storage/cursor-store.js.map +1 -0
- package/dist/storage/local-queue.d.ts +30 -0
- package/dist/storage/local-queue.d.ts.map +1 -0
- package/dist/storage/local-queue.js +70 -0
- package/dist/storage/local-queue.js.map +1 -0
- package/dist/utils/buckets.d.ts +17 -0
- package/dist/utils/buckets.d.ts.map +1 -0
- package/dist/utils/buckets.js +38 -0
- package/dist/utils/buckets.js.map +1 -0
- package/dist/utils/paths.d.ts +17 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +21 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Recursively collect files matching a predicate under a directory.
|
|
5
|
+
* Uses withFileTypes to avoid separate stat() calls per entry.
|
|
6
|
+
* Returns absolute paths sorted alphabetically.
|
|
7
|
+
*/
|
|
8
|
+
async function collectFiles(dir, predicate) {
|
|
9
|
+
const results = [];
|
|
10
|
+
async function walk(currentDir) {
|
|
11
|
+
let entries;
|
|
12
|
+
try {
|
|
13
|
+
entries = await readdir(currentDir, { withFileTypes: true });
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
const fullPath = join(currentDir, entry.name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
await walk(fullPath);
|
|
22
|
+
}
|
|
23
|
+
else if (entry.isFile() && predicate(entry.name)) {
|
|
24
|
+
results.push(fullPath);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
await walk(dir);
|
|
29
|
+
return results.sort();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Discover Claude Code JSONL files.
|
|
33
|
+
* Path pattern: ~/.claude/projects/\*\*\/*.jsonl
|
|
34
|
+
*/
|
|
35
|
+
export async function discoverClaudeFiles(claudeDir) {
|
|
36
|
+
const projectsDir = join(claudeDir, "projects");
|
|
37
|
+
return collectFiles(projectsDir, (name) => name.endsWith(".jsonl"));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Discover Gemini CLI session files.
|
|
41
|
+
* Path pattern: ~/.gemini/tmp/\*\/chats/session-*.json
|
|
42
|
+
*/
|
|
43
|
+
export async function discoverGeminiFiles(geminiDir) {
|
|
44
|
+
const tmpDir = join(geminiDir, "tmp");
|
|
45
|
+
return collectFiles(tmpDir, (name) => name.startsWith("session-") && name.endsWith(".json"));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Discover OpenCode message files with directory-level mtime optimization.
|
|
49
|
+
*
|
|
50
|
+
* Instead of stat()-ing all 66K+ message files, we stat() only the ~3K
|
|
51
|
+
* session directories. If a directory's mtime hasn't changed since last
|
|
52
|
+
* sync, we skip the entire directory (no readdir, no file stat).
|
|
53
|
+
*
|
|
54
|
+
* Path pattern: ~/.local/share/opencode/storage/message/ses_*\/msg_*.json
|
|
55
|
+
*/
|
|
56
|
+
export async function discoverOpenCodeFiles(messageDir, knownDirMtimes) {
|
|
57
|
+
const known = knownDirMtimes ?? {};
|
|
58
|
+
const newDirMtimes = {};
|
|
59
|
+
const files = [];
|
|
60
|
+
let skippedDirs = 0;
|
|
61
|
+
let entries;
|
|
62
|
+
try {
|
|
63
|
+
entries = await readdir(messageDir, { withFileTypes: true });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return { files: [], dirMtimes: {}, skippedDirs: 0 };
|
|
67
|
+
}
|
|
68
|
+
for (const entry of entries) {
|
|
69
|
+
if (!entry.isDirectory())
|
|
70
|
+
continue;
|
|
71
|
+
const dirPath = join(messageDir, entry.name);
|
|
72
|
+
let dirStat;
|
|
73
|
+
try {
|
|
74
|
+
dirStat = await stat(dirPath);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const currentMtime = dirStat.mtimeMs;
|
|
80
|
+
newDirMtimes[dirPath] = currentMtime;
|
|
81
|
+
// Skip directory if mtime unchanged
|
|
82
|
+
if (known[dirPath] === currentMtime) {
|
|
83
|
+
skippedDirs++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
// Directory changed — read its files
|
|
87
|
+
let dirEntries;
|
|
88
|
+
try {
|
|
89
|
+
dirEntries = await readdir(dirPath, { withFileTypes: true });
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
for (const fileEntry of dirEntries) {
|
|
95
|
+
if (fileEntry.isFile() && fileEntry.name.endsWith(".json")) {
|
|
96
|
+
files.push(join(dirPath, fileEntry.name));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { files: files.sort(), dirMtimes: newDirMtimes, skippedDirs };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Discover OpenClaw session files.
|
|
104
|
+
* Path pattern: ~/.openclaw/agents/\*\/sessions/*.jsonl
|
|
105
|
+
*/
|
|
106
|
+
export async function discoverOpenClawFiles(openclawDir) {
|
|
107
|
+
const agentsDir = join(openclawDir, "agents");
|
|
108
|
+
return collectFiles(agentsDir, (name) => name.endsWith(".jsonl"));
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=sources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/discovery/sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;GAIG;AACH,KAAK,UAAU,YAAY,CACzB,GAAW,EACX,SAAoC;IAEpC,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,UAAU,IAAI,CAAC,UAAkB;QACpC,IAAI,OAAmC,CAAC;QACxC,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvB,CAAC;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,SAAiB;IAEjB,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAChD,OAAO,YAAY,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACtE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,SAAiB;IAEjB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,YAAY,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CACnC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CACtD,CAAC;AACJ,CAAC;AAcD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,UAAkB,EAClB,cAAuC;IAEvC,MAAM,KAAK,GAAG,cAAc,IAAI,EAAE,CAAC;IACnC,MAAM,YAAY,GAA2B,EAAE,CAAC;IAChD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,IAAI,OAAmC,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IACtD,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,SAAS;QAEnC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,OAAgC,CAAC;QACrC,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;QACrC,YAAY,CAAC,OAAO,CAAC,GAAG,YAAY,CAAC;QAErC,oCAAoC;QACpC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,YAAY,EAAE,CAAC;YACpC,WAAW,EAAE,CAAC;YACd,SAAS;QACX,CAAC;QAED,qCAAqC;QACrC,IAAI,UAAsC,CAAC;QAC3C,IAAI,CAAC;YACH,UAAU,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,IAAI,SAAS,CAAC,MAAM,EAAE,IAAI,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC3D,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AACvE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,WAAmB;IAEnB,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC9C,OAAO,YAAY,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACpE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Source, TokenDelta } from "@pew/core";
|
|
2
|
+
/** A parsed token delta with metadata for bucket aggregation */
|
|
3
|
+
export interface ParsedDelta {
|
|
4
|
+
source: Source;
|
|
5
|
+
model: string;
|
|
6
|
+
timestamp: string;
|
|
7
|
+
tokens: TokenDelta;
|
|
8
|
+
}
|
|
9
|
+
/** Result of parsing a single Claude JSONL file */
|
|
10
|
+
export interface ClaudeFileResult {
|
|
11
|
+
deltas: ParsedDelta[];
|
|
12
|
+
endOffset: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Normalize Claude's usage object to our TokenDelta format.
|
|
16
|
+
*
|
|
17
|
+
* Claude API fields:
|
|
18
|
+
* input_tokens + cache_creation_input_tokens → inputTokens
|
|
19
|
+
* cache_read_input_tokens → cachedInputTokens
|
|
20
|
+
* output_tokens → outputTokens
|
|
21
|
+
* (hardcoded 0) → reasoningOutputTokens
|
|
22
|
+
*/
|
|
23
|
+
export declare function normalizeClaudeUsage(u: Record<string, unknown>): TokenDelta;
|
|
24
|
+
/**
|
|
25
|
+
* Parse a Claude Code JSONL file incrementally from a byte offset.
|
|
26
|
+
*
|
|
27
|
+
* Each assistant message with non-zero usage produces a standalone delta
|
|
28
|
+
* (no running-total diffing needed — each usage block is absolute).
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseClaudeFile(opts: {
|
|
31
|
+
filePath: string;
|
|
32
|
+
startOffset: number;
|
|
33
|
+
}): Promise<ClaudeFileResult>;
|
|
34
|
+
//# sourceMappingURL=claude.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/parsers/claude.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEpD,gEAAgE;AAChE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AASD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAS3E;AAYD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgE5B"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { createInterface } from "node:readline";
|
|
4
|
+
/** Coerce to non-negative integer, returning 0 for invalid values */
|
|
5
|
+
function toNonNegInt(v) {
|
|
6
|
+
const n = Number(v);
|
|
7
|
+
if (!Number.isFinite(n) || n < 0)
|
|
8
|
+
return 0;
|
|
9
|
+
return Math.floor(n);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Normalize Claude's usage object to our TokenDelta format.
|
|
13
|
+
*
|
|
14
|
+
* Claude API fields:
|
|
15
|
+
* input_tokens + cache_creation_input_tokens → inputTokens
|
|
16
|
+
* cache_read_input_tokens → cachedInputTokens
|
|
17
|
+
* output_tokens → outputTokens
|
|
18
|
+
* (hardcoded 0) → reasoningOutputTokens
|
|
19
|
+
*/
|
|
20
|
+
export function normalizeClaudeUsage(u) {
|
|
21
|
+
return {
|
|
22
|
+
inputTokens: toNonNegInt(u?.input_tokens) +
|
|
23
|
+
toNonNegInt(u?.cache_creation_input_tokens),
|
|
24
|
+
cachedInputTokens: toNonNegInt(u?.cache_read_input_tokens),
|
|
25
|
+
outputTokens: toNonNegInt(u?.output_tokens),
|
|
26
|
+
reasoningOutputTokens: 0,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Check if a TokenDelta is all zeros */
|
|
30
|
+
function isAllZero(delta) {
|
|
31
|
+
return (delta.inputTokens === 0 &&
|
|
32
|
+
delta.cachedInputTokens === 0 &&
|
|
33
|
+
delta.outputTokens === 0 &&
|
|
34
|
+
delta.reasoningOutputTokens === 0);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse a Claude Code JSONL file incrementally from a byte offset.
|
|
38
|
+
*
|
|
39
|
+
* Each assistant message with non-zero usage produces a standalone delta
|
|
40
|
+
* (no running-total diffing needed — each usage block is absolute).
|
|
41
|
+
*/
|
|
42
|
+
export async function parseClaudeFile(opts) {
|
|
43
|
+
const { filePath, startOffset } = opts;
|
|
44
|
+
const deltas = [];
|
|
45
|
+
const st = await stat(filePath).catch(() => null);
|
|
46
|
+
if (!st || !st.isFile())
|
|
47
|
+
return { deltas, endOffset: startOffset };
|
|
48
|
+
const endOffset = st.size;
|
|
49
|
+
if (startOffset >= endOffset)
|
|
50
|
+
return { deltas, endOffset };
|
|
51
|
+
const stream = createReadStream(filePath, {
|
|
52
|
+
encoding: "utf8",
|
|
53
|
+
start: startOffset,
|
|
54
|
+
});
|
|
55
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
56
|
+
try {
|
|
57
|
+
for await (const line of rl) {
|
|
58
|
+
// Fast-path: skip lines that can't contain usage data
|
|
59
|
+
if (!line || !line.includes('"usage"'))
|
|
60
|
+
continue;
|
|
61
|
+
let obj;
|
|
62
|
+
try {
|
|
63
|
+
obj = JSON.parse(line);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Extract usage from message.usage or obj.usage
|
|
69
|
+
const msg = obj?.message;
|
|
70
|
+
const usage = (msg?.usage || obj?.usage);
|
|
71
|
+
if (!usage || typeof usage !== "object")
|
|
72
|
+
continue;
|
|
73
|
+
// Extract model
|
|
74
|
+
const model = typeof (msg?.model ?? obj?.model) === "string"
|
|
75
|
+
? String(msg?.model ?? obj?.model).trim()
|
|
76
|
+
: null;
|
|
77
|
+
if (!model)
|
|
78
|
+
continue;
|
|
79
|
+
// Extract timestamp
|
|
80
|
+
const timestamp = typeof obj?.timestamp === "string" ? obj.timestamp : null;
|
|
81
|
+
if (!timestamp)
|
|
82
|
+
continue;
|
|
83
|
+
// Normalize and filter zero deltas
|
|
84
|
+
const delta = normalizeClaudeUsage(usage);
|
|
85
|
+
if (isAllZero(delta))
|
|
86
|
+
continue;
|
|
87
|
+
deltas.push({
|
|
88
|
+
source: "claude-code",
|
|
89
|
+
model,
|
|
90
|
+
timestamp,
|
|
91
|
+
tokens: delta,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
rl.close();
|
|
97
|
+
stream.destroy();
|
|
98
|
+
}
|
|
99
|
+
return { deltas, endOffset };
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/parsers/claude.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { TokenDelta } from "@pew/core";
|
|
2
|
+
import type { ParsedDelta } from "./claude.js";
|
|
3
|
+
/** Result of parsing a Gemini session JSON file */
|
|
4
|
+
export interface GeminiFileResult {
|
|
5
|
+
deltas: ParsedDelta[];
|
|
6
|
+
lastIndex: number;
|
|
7
|
+
lastTotals: TokenDelta | null;
|
|
8
|
+
lastModel: string | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Normalize Gemini's token object to our TokenDelta format.
|
|
12
|
+
*
|
|
13
|
+
* Gemini fields:
|
|
14
|
+
* input → inputTokens
|
|
15
|
+
* cached → cachedInputTokens
|
|
16
|
+
* output + tool → outputTokens
|
|
17
|
+
* thoughts → reasoningOutputTokens
|
|
18
|
+
*/
|
|
19
|
+
export declare function normalizeGeminiTokens(tokens: Record<string, unknown> | null | undefined): TokenDelta | null;
|
|
20
|
+
/**
|
|
21
|
+
* Diff current cumulative totals against previous.
|
|
22
|
+
* Returns null if no change, or the full current if totals reset (decreased).
|
|
23
|
+
* Shared by Gemini and OpenCode parsers.
|
|
24
|
+
*/
|
|
25
|
+
export declare function diffTotals(current: TokenDelta, previous: TokenDelta | null): TokenDelta | null;
|
|
26
|
+
/**
|
|
27
|
+
* Parse a Gemini CLI session JSON file.
|
|
28
|
+
*
|
|
29
|
+
* Gemini stores one JSON file per session with a `messages[]` array.
|
|
30
|
+
* Each `type: "gemini"` message has cumulative `tokens` — we diff
|
|
31
|
+
* against the previous totals to compute per-message deltas.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseGeminiFile(opts: {
|
|
34
|
+
filePath: string;
|
|
35
|
+
startIndex: number;
|
|
36
|
+
lastTotals: TokenDelta | null;
|
|
37
|
+
}): Promise<GeminiFileResult>;
|
|
38
|
+
//# sourceMappingURL=gemini.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/parsers/gemini.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AASD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GACjD,UAAU,GAAG,IAAI,CASnB;AAiBD;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,UAAU,GAAG,IAAI,GAC1B,UAAU,GAAG,IAAI,CA8BnB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;CAC/B,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmF5B"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
/** Coerce to non-negative integer */
|
|
3
|
+
function toNonNegInt(v) {
|
|
4
|
+
const n = Number(v);
|
|
5
|
+
if (!Number.isFinite(n) || n < 0)
|
|
6
|
+
return 0;
|
|
7
|
+
return Math.floor(n);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Normalize Gemini's token object to our TokenDelta format.
|
|
11
|
+
*
|
|
12
|
+
* Gemini fields:
|
|
13
|
+
* input → inputTokens
|
|
14
|
+
* cached → cachedInputTokens
|
|
15
|
+
* output + tool → outputTokens
|
|
16
|
+
* thoughts → reasoningOutputTokens
|
|
17
|
+
*/
|
|
18
|
+
export function normalizeGeminiTokens(tokens) {
|
|
19
|
+
if (!tokens || typeof tokens !== "object")
|
|
20
|
+
return null;
|
|
21
|
+
return {
|
|
22
|
+
inputTokens: toNonNegInt(tokens.input),
|
|
23
|
+
cachedInputTokens: toNonNegInt(tokens.cached),
|
|
24
|
+
outputTokens: toNonNegInt(tokens.output) + toNonNegInt(tokens.tool),
|
|
25
|
+
reasoningOutputTokens: toNonNegInt(tokens.thoughts),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Check if a TokenDelta is all zeros */
|
|
29
|
+
function isAllZero(d) {
|
|
30
|
+
return (d.inputTokens === 0 &&
|
|
31
|
+
d.cachedInputTokens === 0 &&
|
|
32
|
+
d.outputTokens === 0 &&
|
|
33
|
+
d.reasoningOutputTokens === 0);
|
|
34
|
+
}
|
|
35
|
+
/** Compute total from a TokenDelta (for reset detection) */
|
|
36
|
+
function totalOf(d) {
|
|
37
|
+
return d.inputTokens + d.cachedInputTokens + d.outputTokens + d.reasoningOutputTokens;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Diff current cumulative totals against previous.
|
|
41
|
+
* Returns null if no change, or the full current if totals reset (decreased).
|
|
42
|
+
* Shared by Gemini and OpenCode parsers.
|
|
43
|
+
*/
|
|
44
|
+
export function diffTotals(current, previous) {
|
|
45
|
+
if (!previous)
|
|
46
|
+
return current;
|
|
47
|
+
// Same → no change
|
|
48
|
+
if (current.inputTokens === previous.inputTokens &&
|
|
49
|
+
current.cachedInputTokens === previous.cachedInputTokens &&
|
|
50
|
+
current.outputTokens === previous.outputTokens &&
|
|
51
|
+
current.reasoningOutputTokens === previous.reasoningOutputTokens) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
// Total decreased → reset, treat current as full delta
|
|
55
|
+
if (totalOf(current) < totalOf(previous))
|
|
56
|
+
return current;
|
|
57
|
+
const delta = {
|
|
58
|
+
inputTokens: Math.max(0, current.inputTokens - previous.inputTokens),
|
|
59
|
+
cachedInputTokens: Math.max(0, current.cachedInputTokens - previous.cachedInputTokens),
|
|
60
|
+
outputTokens: Math.max(0, current.outputTokens - previous.outputTokens),
|
|
61
|
+
reasoningOutputTokens: Math.max(0, current.reasoningOutputTokens - previous.reasoningOutputTokens),
|
|
62
|
+
};
|
|
63
|
+
return isAllZero(delta) ? null : delta;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Parse a Gemini CLI session JSON file.
|
|
67
|
+
*
|
|
68
|
+
* Gemini stores one JSON file per session with a `messages[]` array.
|
|
69
|
+
* Each `type: "gemini"` message has cumulative `tokens` — we diff
|
|
70
|
+
* against the previous totals to compute per-message deltas.
|
|
71
|
+
*/
|
|
72
|
+
export async function parseGeminiFile(opts) {
|
|
73
|
+
const { filePath } = opts;
|
|
74
|
+
let { startIndex, lastTotals } = opts;
|
|
75
|
+
const deltas = [];
|
|
76
|
+
let lastModel = null;
|
|
77
|
+
let raw;
|
|
78
|
+
try {
|
|
79
|
+
raw = await readFile(filePath, "utf8");
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return { deltas, lastIndex: startIndex, lastTotals, lastModel };
|
|
83
|
+
}
|
|
84
|
+
if (!raw.trim()) {
|
|
85
|
+
return { deltas, lastIndex: startIndex, lastTotals, lastModel };
|
|
86
|
+
}
|
|
87
|
+
let session;
|
|
88
|
+
try {
|
|
89
|
+
session = JSON.parse(raw);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return { deltas, lastIndex: startIndex, lastTotals, lastModel };
|
|
93
|
+
}
|
|
94
|
+
const messages = Array.isArray(session?.messages)
|
|
95
|
+
? session.messages
|
|
96
|
+
: [];
|
|
97
|
+
// Reset if lastIndex is beyond the array (file rewrite)
|
|
98
|
+
if (startIndex >= messages.length) {
|
|
99
|
+
startIndex = -1;
|
|
100
|
+
lastTotals = null;
|
|
101
|
+
}
|
|
102
|
+
let totals = lastTotals;
|
|
103
|
+
let model = lastModel;
|
|
104
|
+
const begin = startIndex + 1;
|
|
105
|
+
for (let idx = begin; idx < messages.length; idx++) {
|
|
106
|
+
const msg = messages[idx];
|
|
107
|
+
if (!msg || typeof msg !== "object")
|
|
108
|
+
continue;
|
|
109
|
+
// Track model
|
|
110
|
+
const msgModel = typeof msg.model === "string" ? msg.model.trim() : null;
|
|
111
|
+
if (msgModel)
|
|
112
|
+
model = msgModel;
|
|
113
|
+
// Extract timestamp
|
|
114
|
+
const timestamp = typeof msg.timestamp === "string" ? msg.timestamp : null;
|
|
115
|
+
// Normalize tokens
|
|
116
|
+
const currentTotals = normalizeGeminiTokens(msg.tokens);
|
|
117
|
+
if (!timestamp || !currentTotals) {
|
|
118
|
+
totals = currentTotals || totals;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
// Compute delta
|
|
122
|
+
const delta = diffTotals(currentTotals, totals);
|
|
123
|
+
if (!delta || isAllZero(delta)) {
|
|
124
|
+
totals = currentTotals;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
deltas.push({
|
|
128
|
+
source: "gemini-cli",
|
|
129
|
+
model: model || "unknown",
|
|
130
|
+
timestamp,
|
|
131
|
+
tokens: delta,
|
|
132
|
+
});
|
|
133
|
+
totals = currentTotals;
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
deltas,
|
|
137
|
+
lastIndex: messages.length - 1,
|
|
138
|
+
lastTotals: totals,
|
|
139
|
+
lastModel: model,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=gemini.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/parsers/gemini.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ParsedDelta } from "./claude.js";
|
|
2
|
+
/** Result of parsing an OpenClaw JSONL session file */
|
|
3
|
+
export interface OpenClawFileResult {
|
|
4
|
+
deltas: ParsedDelta[];
|
|
5
|
+
endOffset: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parse an OpenClaw session JSONL file incrementally from a byte offset.
|
|
9
|
+
*
|
|
10
|
+
* OpenClaw writes one JSONL line per event. We look for `type: "message"`
|
|
11
|
+
* lines with `message.usage` containing absolute token counts (no diffing needed).
|
|
12
|
+
*
|
|
13
|
+
* OpenClaw normalization:
|
|
14
|
+
* input → inputTokens
|
|
15
|
+
* cacheRead + cacheWrite → cachedInputTokens
|
|
16
|
+
* output → outputTokens
|
|
17
|
+
* (hardcoded 0) → reasoningOutputTokens
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseOpenClawFile(opts: {
|
|
20
|
+
filePath: string;
|
|
21
|
+
startOffset: number;
|
|
22
|
+
}): Promise<OpenClawFileResult>;
|
|
23
|
+
//# sourceMappingURL=openclaw.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclaw.d.ts","sourceRoot":"","sources":["../../src/parsers/openclaw.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,uDAAuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAYD;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAmE9B"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { createInterface } from "node:readline";
|
|
4
|
+
/** Check if a TokenDelta is all zeros */
|
|
5
|
+
function isAllZero(d) {
|
|
6
|
+
return (d.inputTokens === 0 &&
|
|
7
|
+
d.cachedInputTokens === 0 &&
|
|
8
|
+
d.outputTokens === 0 &&
|
|
9
|
+
d.reasoningOutputTokens === 0);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Parse an OpenClaw session JSONL file incrementally from a byte offset.
|
|
13
|
+
*
|
|
14
|
+
* OpenClaw writes one JSONL line per event. We look for `type: "message"`
|
|
15
|
+
* lines with `message.usage` containing absolute token counts (no diffing needed).
|
|
16
|
+
*
|
|
17
|
+
* OpenClaw normalization:
|
|
18
|
+
* input → inputTokens
|
|
19
|
+
* cacheRead + cacheWrite → cachedInputTokens
|
|
20
|
+
* output → outputTokens
|
|
21
|
+
* (hardcoded 0) → reasoningOutputTokens
|
|
22
|
+
*/
|
|
23
|
+
export async function parseOpenClawFile(opts) {
|
|
24
|
+
const { filePath, startOffset } = opts;
|
|
25
|
+
const deltas = [];
|
|
26
|
+
const st = await stat(filePath).catch(() => null);
|
|
27
|
+
if (!st || !st.isFile())
|
|
28
|
+
return { deltas, endOffset: startOffset };
|
|
29
|
+
const endOffset = st.size;
|
|
30
|
+
if (startOffset >= endOffset)
|
|
31
|
+
return { deltas, endOffset };
|
|
32
|
+
const stream = createReadStream(filePath, {
|
|
33
|
+
encoding: "utf8",
|
|
34
|
+
start: startOffset,
|
|
35
|
+
});
|
|
36
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
37
|
+
try {
|
|
38
|
+
for await (const line of rl) {
|
|
39
|
+
if (!line)
|
|
40
|
+
continue;
|
|
41
|
+
// Fast-path: OpenClaw messages include "usage" and "totalTokens"
|
|
42
|
+
if (!line.includes('"usage"') || !line.includes("totalTokens"))
|
|
43
|
+
continue;
|
|
44
|
+
let obj;
|
|
45
|
+
try {
|
|
46
|
+
obj = JSON.parse(line);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (obj?.type !== "message")
|
|
52
|
+
continue;
|
|
53
|
+
const msg = obj?.message;
|
|
54
|
+
if (!msg || typeof msg !== "object")
|
|
55
|
+
continue;
|
|
56
|
+
const usage = msg.usage;
|
|
57
|
+
if (!usage || typeof usage !== "object")
|
|
58
|
+
continue;
|
|
59
|
+
const timestamp = typeof obj?.timestamp === "string" ? obj.timestamp : null;
|
|
60
|
+
if (!timestamp)
|
|
61
|
+
continue;
|
|
62
|
+
const model = typeof msg.model === "string" ? msg.model.trim() : "unknown";
|
|
63
|
+
const tokens = {
|
|
64
|
+
inputTokens: Number(usage.input || 0),
|
|
65
|
+
cachedInputTokens: Number(usage.cacheRead || 0) + Number(usage.cacheWrite || 0),
|
|
66
|
+
outputTokens: Number(usage.output || 0),
|
|
67
|
+
reasoningOutputTokens: 0,
|
|
68
|
+
};
|
|
69
|
+
if (isAllZero(tokens))
|
|
70
|
+
continue;
|
|
71
|
+
deltas.push({
|
|
72
|
+
source: "openclaw",
|
|
73
|
+
model,
|
|
74
|
+
timestamp,
|
|
75
|
+
tokens,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
rl.close();
|
|
81
|
+
stream.destroy();
|
|
82
|
+
}
|
|
83
|
+
return { deltas, endOffset };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=openclaw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclaw.js","sourceRoot":"","sources":["../../src/parsers/openclaw.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TokenDelta } from "@pew/core";
|
|
2
|
+
import type { ParsedDelta } from "./claude.js";
|
|
3
|
+
/** Result of parsing a single OpenCode message file */
|
|
4
|
+
export interface OpenCodeFileResult {
|
|
5
|
+
delta: ParsedDelta | null;
|
|
6
|
+
messageKey: string | null;
|
|
7
|
+
lastTotals: TokenDelta | null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Coerce an epoch value to milliseconds.
|
|
11
|
+
* Values < 1e12 are treated as seconds and multiplied by 1000.
|
|
12
|
+
*/
|
|
13
|
+
export declare function coerceEpochMs(v: unknown): number;
|
|
14
|
+
/**
|
|
15
|
+
* Normalize OpenCode's token object to our TokenDelta format.
|
|
16
|
+
*
|
|
17
|
+
* OpenCode fields:
|
|
18
|
+
* input + cache.write → inputTokens
|
|
19
|
+
* cache.read → cachedInputTokens
|
|
20
|
+
* output → outputTokens
|
|
21
|
+
* reasoning → reasoningOutputTokens
|
|
22
|
+
*/
|
|
23
|
+
export declare function normalizeOpenCodeTokens(tokens: Record<string, unknown> | null | undefined): TokenDelta | null;
|
|
24
|
+
/**
|
|
25
|
+
* Parse a single OpenCode message JSON file.
|
|
26
|
+
*
|
|
27
|
+
* Each file is a standalone JSON object for one message.
|
|
28
|
+
* Uses diff against previous totals to compute incremental deltas
|
|
29
|
+
* (reuses diffTotals from Gemini parser — same logic).
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseOpenCodeFile(opts: {
|
|
32
|
+
filePath: string;
|
|
33
|
+
lastTotals: TokenDelta | null;
|
|
34
|
+
}): Promise<OpenCodeFileResult>;
|
|
35
|
+
//# sourceMappingURL=opencode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../src/parsers/opencode.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,uDAAuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;CAC/B;AASD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAKhD;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GACjD,UAAU,GAAG,IAAI,CAanB;AAYD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;CAC/B,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAwE9B"}
|