@ken-jo/agent-connector 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +6 -0
- package/README.md +348 -0
- package/dist/amp-QSFH2V7Y.js +217 -0
- package/dist/amp-QSFH2V7Y.js.map +1 -0
- package/dist/amp-TULUPXXL.js +237 -0
- package/dist/amp-TULUPXXL.js.map +1 -0
- package/dist/antigravity-CX2YLSY7.js +17 -0
- package/dist/antigravity-CX2YLSY7.js.map +1 -0
- package/dist/antigravity-STHZWMP3.js +171 -0
- package/dist/antigravity-STHZWMP3.js.map +1 -0
- package/dist/antigravity-cli-P6HADOAO.js +121 -0
- package/dist/antigravity-cli-P6HADOAO.js.map +1 -0
- package/dist/antigravity-cli-YQDFVV4U.js +86 -0
- package/dist/antigravity-cli-YQDFVV4U.js.map +1 -0
- package/dist/chunk-54EMKTTX.js +191 -0
- package/dist/chunk-54EMKTTX.js.map +1 -0
- package/dist/chunk-7L6ITC4T.js +54 -0
- package/dist/chunk-7L6ITC4T.js.map +1 -0
- package/dist/chunk-7MSDGHJW.js +316 -0
- package/dist/chunk-7MSDGHJW.js.map +1 -0
- package/dist/chunk-BCHWFTV4.js +196 -0
- package/dist/chunk-BCHWFTV4.js.map +1 -0
- package/dist/chunk-DBQMX75U.js +36 -0
- package/dist/chunk-DBQMX75U.js.map +1 -0
- package/dist/chunk-EXLS7BSI.js +29 -0
- package/dist/chunk-EXLS7BSI.js.map +1 -0
- package/dist/chunk-JNM3TPLX.js +593 -0
- package/dist/chunk-JNM3TPLX.js.map +1 -0
- package/dist/chunk-JWNT5KW7.js +197 -0
- package/dist/chunk-JWNT5KW7.js.map +1 -0
- package/dist/chunk-KHO5WNTP.js +30 -0
- package/dist/chunk-KHO5WNTP.js.map +1 -0
- package/dist/chunk-LTT56GI3.js +155 -0
- package/dist/chunk-LTT56GI3.js.map +1 -0
- package/dist/chunk-MEH7GC7R.js +111 -0
- package/dist/chunk-MEH7GC7R.js.map +1 -0
- package/dist/chunk-MQPNU2CY.js +64 -0
- package/dist/chunk-MQPNU2CY.js.map +1 -0
- package/dist/chunk-OPXAP3AC.js +12 -0
- package/dist/chunk-OPXAP3AC.js.map +1 -0
- package/dist/chunk-RK6VBLXP.js +50 -0
- package/dist/chunk-RK6VBLXP.js.map +1 -0
- package/dist/chunk-TIIP3RG2.js +420 -0
- package/dist/chunk-TIIP3RG2.js.map +1 -0
- package/dist/chunk-TRKWUNO4.js +702 -0
- package/dist/chunk-TRKWUNO4.js.map +1 -0
- package/dist/chunk-TRQKHP7D.js +93 -0
- package/dist/chunk-TRQKHP7D.js.map +1 -0
- package/dist/chunk-WCETQWWK.js +59 -0
- package/dist/chunk-WCETQWWK.js.map +1 -0
- package/dist/chunk-WV63X7IK.js +357 -0
- package/dist/chunk-WV63X7IK.js.map +1 -0
- package/dist/chunk-XD7MY6HB.js +343 -0
- package/dist/chunk-XD7MY6HB.js.map +1 -0
- package/dist/chunk-XNAM7F35.js +276 -0
- package/dist/chunk-XNAM7F35.js.map +1 -0
- package/dist/chunk-XUCWLJY5.js +238 -0
- package/dist/chunk-XUCWLJY5.js.map +1 -0
- package/dist/chunk-YNZU6PAV.js +800 -0
- package/dist/chunk-YNZU6PAV.js.map +1 -0
- package/dist/chunk-YT43MUSJ.js +146 -0
- package/dist/chunk-YT43MUSJ.js.map +1 -0
- package/dist/claude-code-HKDV7T32.js +637 -0
- package/dist/claude-code-HKDV7T32.js.map +1 -0
- package/dist/claude-code-W74MNTAC.js +652 -0
- package/dist/claude-code-W74MNTAC.js.map +1 -0
- package/dist/cli/sdk.d.ts +54 -0
- package/dist/cli/sdk.js +82 -0
- package/dist/cli/sdk.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -0
- package/dist/codebuff-3PMZJJHP.js +307 -0
- package/dist/codebuff-3PMZJJHP.js.map +1 -0
- package/dist/codebuff-QX57FJDN.js +220 -0
- package/dist/codebuff-QX57FJDN.js.map +1 -0
- package/dist/codex-JFQTS6LU.js +593 -0
- package/dist/codex-JFQTS6LU.js.map +1 -0
- package/dist/codex-Q3ATRRX3.js +364 -0
- package/dist/codex-Q3ATRRX3.js.map +1 -0
- package/dist/copilot-cli-MTCGHZ2T.js +622 -0
- package/dist/copilot-cli-MTCGHZ2T.js.map +1 -0
- package/dist/copilot-cli-SJGIXVSD.js +369 -0
- package/dist/copilot-cli-SJGIXVSD.js.map +1 -0
- package/dist/crush-I7PK3FGI.js +360 -0
- package/dist/crush-I7PK3FGI.js.map +1 -0
- package/dist/crush-LN5QFFR2.js +183 -0
- package/dist/crush-LN5QFFR2.js.map +1 -0
- package/dist/cursor-4TMXKJOZ.js +603 -0
- package/dist/cursor-4TMXKJOZ.js.map +1 -0
- package/dist/cursor-QZ6MTO74.js +194 -0
- package/dist/cursor-QZ6MTO74.js.map +1 -0
- package/dist/detect-WIAGVYS4.js +68 -0
- package/dist/detect-WIAGVYS4.js.map +1 -0
- package/dist/doctor-EZW2K4NI.js +372 -0
- package/dist/doctor-EZW2K4NI.js.map +1 -0
- package/dist/droid-KN6VRMMT.js +424 -0
- package/dist/droid-KN6VRMMT.js.map +1 -0
- package/dist/droid-W5WF5XEX.js +224 -0
- package/dist/droid-W5WF5XEX.js.map +1 -0
- package/dist/gemini-cli-CIRW37TU.js +369 -0
- package/dist/gemini-cli-CIRW37TU.js.map +1 -0
- package/dist/gemini-cli-Y35FFK4M.js +705 -0
- package/dist/gemini-cli-Y35FFK4M.js.map +1 -0
- package/dist/goose-A3M5GODH.js +212 -0
- package/dist/goose-A3M5GODH.js.map +1 -0
- package/dist/goose-ZTGTSUFJ.js +480 -0
- package/dist/goose-ZTGTSUFJ.js.map +1 -0
- package/dist/hermes-H2KV44GK.js +470 -0
- package/dist/hermes-H2KV44GK.js.map +1 -0
- package/dist/hermes-ZYLMOFLZ.js +196 -0
- package/dist/hermes-ZYLMOFLZ.js.map +1 -0
- package/dist/hook-YVDILAMZ.js +75 -0
- package/dist/hook-YVDILAMZ.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/install-HQRYZOQ2.js +60 -0
- package/dist/install-HQRYZOQ2.js.map +1 -0
- package/dist/jetbrains-copilot-HWA3DMH7.js +633 -0
- package/dist/jetbrains-copilot-HWA3DMH7.js.map +1 -0
- package/dist/kilo-NSWAVP6P.js +235 -0
- package/dist/kilo-NSWAVP6P.js.map +1 -0
- package/dist/kilo-QXJRCSLT.js +384 -0
- package/dist/kilo-QXJRCSLT.js.map +1 -0
- package/dist/kilo-cli-EAQTHXUW.js +587 -0
- package/dist/kilo-cli-EAQTHXUW.js.map +1 -0
- package/dist/kilo-cli-EVBKUODA.js +160 -0
- package/dist/kilo-cli-EVBKUODA.js.map +1 -0
- package/dist/kimi-EI3UM3ZV.js +155 -0
- package/dist/kimi-EI3UM3ZV.js.map +1 -0
- package/dist/kimi-GQ7VXNYT.js +423 -0
- package/dist/kimi-GQ7VXNYT.js.map +1 -0
- package/dist/kiro-UCLSLBW2.js +217 -0
- package/dist/kiro-UCLSLBW2.js.map +1 -0
- package/dist/kiro-VQTT6LO2.js +451 -0
- package/dist/kiro-VQTT6LO2.js.map +1 -0
- package/dist/leaderboard-K6ON3SWW.js +137 -0
- package/dist/leaderboard-K6ON3SWW.js.map +1 -0
- package/dist/mux-T2TV4CIQ.js +211 -0
- package/dist/mux-T2TV4CIQ.js.map +1 -0
- package/dist/mux-ZMLVAXOO.js +96 -0
- package/dist/mux-ZMLVAXOO.js.map +1 -0
- package/dist/omp-PB4VDKS6.js +586 -0
- package/dist/omp-PB4VDKS6.js.map +1 -0
- package/dist/openclaw-ETN2M63O.js +751 -0
- package/dist/openclaw-ETN2M63O.js.map +1 -0
- package/dist/openclaw-IYA7ASL6.js +150 -0
- package/dist/openclaw-IYA7ASL6.js.map +1 -0
- package/dist/opencode-FZ33JKUZ.js +734 -0
- package/dist/opencode-FZ33JKUZ.js.map +1 -0
- package/dist/opencode-HWT3RSMN.js +216 -0
- package/dist/opencode-HWT3RSMN.js.map +1 -0
- package/dist/package-DLOVE2E4.js +1132 -0
- package/dist/package-DLOVE2E4.js.map +1 -0
- package/dist/pi-CIU2QHT4.js +208 -0
- package/dist/pi-CIU2QHT4.js.map +1 -0
- package/dist/pi-UFX2OYFV.js +139 -0
- package/dist/pi-UFX2OYFV.js.map +1 -0
- package/dist/qwen-code-5POCNU6P.js +573 -0
- package/dist/qwen-code-5POCNU6P.js.map +1 -0
- package/dist/qwen-code-W7NUCDEN.js +134 -0
- package/dist/qwen-code-W7NUCDEN.js.map +1 -0
- package/dist/roo-code-IOIUXRDE.js +242 -0
- package/dist/roo-code-IOIUXRDE.js.map +1 -0
- package/dist/roo-code-ZLKH4A7N.js +229 -0
- package/dist/roo-code-ZLKH4A7N.js.map +1 -0
- package/dist/runtime/index.d.ts +157 -0
- package/dist/runtime/index.js +20 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/serve-HVWWY4B6.js +65 -0
- package/dist/serve-HVWWY4B6.js.map +1 -0
- package/dist/status-FE7TECZD.js +128 -0
- package/dist/status-FE7TECZD.js.map +1 -0
- package/dist/synthetic-UKR6SMGS.js +178 -0
- package/dist/synthetic-UKR6SMGS.js.map +1 -0
- package/dist/telemetry-36GESKQQ.js +349 -0
- package/dist/telemetry-36GESKQQ.js.map +1 -0
- package/dist/trae-JSNDZFKL.js +204 -0
- package/dist/trae-JSNDZFKL.js.map +1 -0
- package/dist/trae-PA26DPBW.js +157 -0
- package/dist/trae-PA26DPBW.js.map +1 -0
- package/dist/types-CYUB1OTr.d.ts +427 -0
- package/dist/uninstall-IGCKNOSF.js +72 -0
- package/dist/uninstall-IGCKNOSF.js.map +1 -0
- package/dist/upgrade-JV2VK242.js +104 -0
- package/dist/upgrade-JV2VK242.js.map +1 -0
- package/dist/usage-6ER6EMEY.js +324 -0
- package/dist/usage-6ER6EMEY.js.map +1 -0
- package/dist/usage-event-JKBI6XCF.js +48 -0
- package/dist/usage-event-JKBI6XCF.js.map +1 -0
- package/dist/vscode-copilot-CK3NO54M.js +715 -0
- package/dist/vscode-copilot-CK3NO54M.js.map +1 -0
- package/dist/warp-JNQA7PBU.js +126 -0
- package/dist/warp-JNQA7PBU.js.map +1 -0
- package/dist/warp-QNVDXOG4.js +199 -0
- package/dist/warp-QNVDXOG4.js.map +1 -0
- package/dist/zed-NNNX5MNT.js +272 -0
- package/dist/zed-NNNX5MNT.js.map +1 -0
- package/dist/zed-VRJKWYUR.js +209 -0
- package/dist/zed-VRJKWYUR.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
readJsonFile,
|
|
4
|
+
readJsonlLines
|
|
5
|
+
} from "./chunk-RK6VBLXP.js";
|
|
6
|
+
import {
|
|
7
|
+
firstExistingRoot,
|
|
8
|
+
walkFiles
|
|
9
|
+
} from "./chunk-JWNT5KW7.js";
|
|
10
|
+
import {
|
|
11
|
+
emptyTokens,
|
|
12
|
+
inferProvider
|
|
13
|
+
} from "./chunk-XD7MY6HB.js";
|
|
14
|
+
|
|
15
|
+
// src/usage/readers/droid.ts
|
|
16
|
+
import { statSync } from "fs";
|
|
17
|
+
import { basename } from "path";
|
|
18
|
+
var PLATFORM_ID = "droid";
|
|
19
|
+
var DEFAULT_PROVIDER = "unknown";
|
|
20
|
+
var JSONL_MODEL_SCAN_LIMIT = 500;
|
|
21
|
+
function toNonNegInt(v) {
|
|
22
|
+
const n = typeof v === "number" ? v : typeof v === "string" ? Number(v) : NaN;
|
|
23
|
+
if (!Number.isFinite(n)) return 0;
|
|
24
|
+
return Math.max(0, Math.trunc(n));
|
|
25
|
+
}
|
|
26
|
+
function normalizeModelName(model) {
|
|
27
|
+
let normalized = model.startsWith("custom:") ? model.slice("custom:".length) : model;
|
|
28
|
+
let stripped = "";
|
|
29
|
+
let inBracket = false;
|
|
30
|
+
for (const ch of normalized) {
|
|
31
|
+
if (ch === "[") inBracket = true;
|
|
32
|
+
else if (ch === "]") inBracket = false;
|
|
33
|
+
else if (!inBracket) stripped += ch;
|
|
34
|
+
}
|
|
35
|
+
normalized = stripped;
|
|
36
|
+
normalized = normalized.replace(/-+$/, "");
|
|
37
|
+
normalized = normalized.toLowerCase();
|
|
38
|
+
normalized = normalized.replace(/\./g, "-");
|
|
39
|
+
normalized = normalized.replace(/-+/g, "-");
|
|
40
|
+
return normalized;
|
|
41
|
+
}
|
|
42
|
+
function canonicalizeProviderSegment(segment) {
|
|
43
|
+
const normalized = segment.trim().replace(/\/+$/, "").toLowerCase().replace(/-/g, "_");
|
|
44
|
+
if (normalized.startsWith("<") && normalized.endsWith(">")) return void 0;
|
|
45
|
+
switch (normalized) {
|
|
46
|
+
case "":
|
|
47
|
+
case "unknown":
|
|
48
|
+
return void 0;
|
|
49
|
+
case "x_ai":
|
|
50
|
+
case "xai":
|
|
51
|
+
return "xai";
|
|
52
|
+
case "z_ai":
|
|
53
|
+
case "zai":
|
|
54
|
+
return "zai";
|
|
55
|
+
case "moonshot":
|
|
56
|
+
case "moonshotai":
|
|
57
|
+
return "moonshotai";
|
|
58
|
+
case "meta":
|
|
59
|
+
case "meta_llama":
|
|
60
|
+
return "meta_llama";
|
|
61
|
+
case "azure":
|
|
62
|
+
case "azure_ai":
|
|
63
|
+
return "azure_ai";
|
|
64
|
+
case "anthropic":
|
|
65
|
+
case "vertex":
|
|
66
|
+
case "vertex_ai":
|
|
67
|
+
return "anthropic";
|
|
68
|
+
case "together":
|
|
69
|
+
case "together_ai":
|
|
70
|
+
return "together_ai";
|
|
71
|
+
case "fireworks":
|
|
72
|
+
case "fireworks_ai":
|
|
73
|
+
return "fireworks_ai";
|
|
74
|
+
case "google":
|
|
75
|
+
case "gemini":
|
|
76
|
+
return "google";
|
|
77
|
+
case "openai":
|
|
78
|
+
case "openai_codex":
|
|
79
|
+
return "openai";
|
|
80
|
+
case "minimax":
|
|
81
|
+
case "minimaxai":
|
|
82
|
+
case "minimax_ai":
|
|
83
|
+
return "minimax";
|
|
84
|
+
case "mistral":
|
|
85
|
+
case "mistralai":
|
|
86
|
+
return "mistralai";
|
|
87
|
+
case "ai21":
|
|
88
|
+
return "ai21";
|
|
89
|
+
default:
|
|
90
|
+
if (/[0-9]/.test(normalized)) return void 0;
|
|
91
|
+
return normalized;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function canonicalProvider(raw) {
|
|
95
|
+
for (const segment of raw.trim().replace(/\/+$/, "").split("/")) {
|
|
96
|
+
const tag = canonicalizeProviderSegment(segment);
|
|
97
|
+
if (tag !== void 0) return tag;
|
|
98
|
+
if (segment.includes(".")) {
|
|
99
|
+
for (const dotted of segment.split(".")) {
|
|
100
|
+
const dottedTag = canonicalizeProviderSegment(dotted);
|
|
101
|
+
if (dottedTag !== void 0) return dottedTag;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return raw;
|
|
106
|
+
}
|
|
107
|
+
function defaultModelFromProvider(provider) {
|
|
108
|
+
switch (canonicalProvider(provider)) {
|
|
109
|
+
case "anthropic":
|
|
110
|
+
return "claude-unknown";
|
|
111
|
+
case "openai":
|
|
112
|
+
return "gpt-unknown";
|
|
113
|
+
case "google":
|
|
114
|
+
return "gemini-unknown";
|
|
115
|
+
case "xai":
|
|
116
|
+
return "grok-unknown";
|
|
117
|
+
default:
|
|
118
|
+
return `${provider}-unknown`;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function extractModelFromJsonl(jsonlPath) {
|
|
122
|
+
const lines = readJsonlLines(jsonlPath);
|
|
123
|
+
const scan = lines.slice(0, JSONL_MODEL_SCAN_LIMIT);
|
|
124
|
+
for (const obj of scan) {
|
|
125
|
+
const text = typeof obj === "string" ? obj : JSON.stringify(obj);
|
|
126
|
+
const pos = text.indexOf("Model:");
|
|
127
|
+
if (pos < 0) continue;
|
|
128
|
+
const after = text.slice(pos + "Model:".length);
|
|
129
|
+
let modelPart = "";
|
|
130
|
+
for (const ch of after) {
|
|
131
|
+
if (ch === "[" || ch === "\\" || ch === '"') break;
|
|
132
|
+
modelPart += ch;
|
|
133
|
+
}
|
|
134
|
+
const trimmed = modelPart.trim();
|
|
135
|
+
if (trimmed !== "") return normalizeModelName(trimmed);
|
|
136
|
+
}
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
function fileMtimeOrZero(path) {
|
|
140
|
+
try {
|
|
141
|
+
return Math.floor(statSync(path).mtimeMs);
|
|
142
|
+
} catch {
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function sessionIdFromPath(path) {
|
|
147
|
+
const name = basename(path);
|
|
148
|
+
const dot = name.lastIndexOf(".");
|
|
149
|
+
const stem = dot > 0 ? name.slice(0, dot) : name;
|
|
150
|
+
const id = stem.replace(/\.settings$/, "");
|
|
151
|
+
return id === "" ? "unknown" : id;
|
|
152
|
+
}
|
|
153
|
+
function parseDroidFile(path) {
|
|
154
|
+
const raw = readJsonFile(path);
|
|
155
|
+
if (typeof raw !== "object" || raw === null) return [];
|
|
156
|
+
const settings = raw;
|
|
157
|
+
const usage = settings.tokenUsage;
|
|
158
|
+
if (usage === void 0 || usage === null || typeof usage !== "object") return [];
|
|
159
|
+
const input = toNonNegInt(usage.inputTokens);
|
|
160
|
+
const output = toNonNegInt(usage.outputTokens);
|
|
161
|
+
const cacheWrite = toNonNegInt(usage.cacheCreationTokens);
|
|
162
|
+
const cacheRead = toNonNegInt(usage.cacheReadTokens);
|
|
163
|
+
const reasoning = toNonNegInt(usage.thinkingTokens);
|
|
164
|
+
if (input + output + cacheWrite + cacheRead + reasoning === 0) return [];
|
|
165
|
+
const sessionId = sessionIdFromPath(path);
|
|
166
|
+
const providerLock = typeof settings.providerLock === "string" && settings.providerLock !== "" ? settings.providerLock : void 0;
|
|
167
|
+
const modelField = typeof settings.model === "string" ? settings.model : void 0;
|
|
168
|
+
const providerId = providerLock ?? inferProvider(modelField ?? "") ?? DEFAULT_PROVIDER;
|
|
169
|
+
let modelId;
|
|
170
|
+
if (modelField !== void 0) {
|
|
171
|
+
modelId = normalizeModelName(modelField);
|
|
172
|
+
} else {
|
|
173
|
+
const jsonlPath = path.replace(/\.settings\.json$/, ".jsonl");
|
|
174
|
+
modelId = jsonlPath !== path ? extractModelFromJsonl(jsonlPath) ?? defaultModelFromProvider(providerId) : defaultModelFromProvider(providerId);
|
|
175
|
+
}
|
|
176
|
+
let ts = 0;
|
|
177
|
+
if (typeof settings.providerLockTimestamp === "string") {
|
|
178
|
+
const parsed = Date.parse(settings.providerLockTimestamp);
|
|
179
|
+
if (!Number.isNaN(parsed)) ts = parsed;
|
|
180
|
+
}
|
|
181
|
+
if (ts === 0) ts = fileMtimeOrZero(path);
|
|
182
|
+
if (ts === 0) return [];
|
|
183
|
+
const tokens = emptyTokens();
|
|
184
|
+
tokens.input = input;
|
|
185
|
+
tokens.output = output;
|
|
186
|
+
tokens.cacheRead = cacheRead;
|
|
187
|
+
tokens.cacheWrite = cacheWrite;
|
|
188
|
+
tokens.reasoning = reasoning;
|
|
189
|
+
return [
|
|
190
|
+
{
|
|
191
|
+
platformId: PLATFORM_ID,
|
|
192
|
+
modelId,
|
|
193
|
+
providerId,
|
|
194
|
+
sessionId,
|
|
195
|
+
tokens,
|
|
196
|
+
ts,
|
|
197
|
+
messageCount: 1,
|
|
198
|
+
confidence: "host-reported"
|
|
199
|
+
}
|
|
200
|
+
];
|
|
201
|
+
}
|
|
202
|
+
var droidReader = {
|
|
203
|
+
platformId: PLATFORM_ID,
|
|
204
|
+
kind: "local",
|
|
205
|
+
async read({ sinceMs }) {
|
|
206
|
+
const root = firstExistingRoot(PLATFORM_ID);
|
|
207
|
+
if (root === void 0) return [];
|
|
208
|
+
const files = walkFiles(root, (name) => name.endsWith(".settings.json"));
|
|
209
|
+
const records = [];
|
|
210
|
+
for (const file of files) {
|
|
211
|
+
const rows = parseDroidFile(file);
|
|
212
|
+
for (const row of rows) {
|
|
213
|
+
if (sinceMs !== void 0 && row.ts < sinceMs) continue;
|
|
214
|
+
records.push(row);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return records;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
var droid_default = droidReader;
|
|
221
|
+
export {
|
|
222
|
+
droid_default as default
|
|
223
|
+
};
|
|
224
|
+
//# sourceMappingURL=droid-W5WF5XEX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/usage/readers/droid.ts"],"sourcesContent":["/**\n * usage/readers/droid — Droid (Factory.ai) native session-settings reader.\n *\n * Faithful port of tokscale sessions/droid.rs. Reads\n * ~/.factory/sessions/*.settings.json. Each settings file is a single JSON\n * object carrying a cumulative `tokenUsage` block for one session:\n * tokenUsage.inputTokens → input\n * tokenUsage.outputTokens → output\n * tokenUsage.cacheReadTokens → cacheRead\n * tokenUsage.cacheCreationTokens → cacheWrite\n * tokenUsage.thinkingTokens → reasoning\n * A file with no tokenUsage, or whose five dimensions sum to 0, yields no row.\n *\n * Model id comes from the `model` field run through Droid's custom normalization\n * (strip a `custom:` prefix, remove [bracket] groups, trim trailing hyphens,\n * lowercase, dots→hyphens, collapse repeated hyphens — e.g.\n * \"custom:Claude-Opus-4.5-Thinking-[Anthropic]-0\" → \"claude-opus-4-5-thinking-0\").\n * When `model` is absent it falls back to scanning the sibling .jsonl\n * (<id>.settings.json → <id>.jsonl) for a `Model:` system-reminder pattern, and\n * finally to a provider-based default (anthropic→claude-unknown, openai→gpt-unknown,\n * google→gemini-unknown, xai→grok-unknown, else <provider>-unknown).\n *\n * Provider prefers the `providerLock` field; otherwise it is inferred from the\n * model (defaulting to \"unknown\", matching the Rust `unwrap_or(\"unknown\")`).\n *\n * Session id is the filename stem with `.settings` stripped\n * (uuid.settings.json → uuid). Timestamp prefers `providerLockTimestamp`\n * (RFC3339 → epoch ms), falling back to the file mtime; a row with no usable\n * timestamp (0) is dropped — to match the Rust mtime-or-0 path, an unreadable\n * mtime is treated as 0 (not \"now\"), so we do not synthesize a timestamp here.\n *\n * No dedup (the session id is the primary key) and no project attribution\n * (Droid settings carry neither). Confidence is \"host-reported\".\n *\n * Fail-open: no root → []; unreadable/malformed file → skipped.\n */\n\nimport { statSync } from \"node:fs\";\nimport { basename } from \"node:path\";\n\nimport type { UsageReader, UsageRecord } from \"../types.js\";\nimport { emptyTokens } from \"../aggregate.js\";\nimport { readJsonFile, readJsonlLines } from \"../jsonl.js\";\nimport { inferProvider } from \"../normalize.js\";\nimport { firstExistingRoot, walkFiles } from \"../paths.js\";\n\nconst PLATFORM_ID = \"droid\" as const;\nconst DEFAULT_PROVIDER = \"unknown\";\n/** Cap matching the Rust scan (avoids slurping a huge sibling .jsonl). */\nconst JSONL_MODEL_SCAN_LIMIT = 500;\n\n/** The fields we read off a Droid settings.json file (all optional / unknown). */\ninterface DroidSettings {\n model?: unknown;\n providerLock?: unknown;\n providerLockTimestamp?: unknown;\n tokenUsage?: {\n inputTokens?: unknown;\n outputTokens?: unknown;\n cacheCreationTokens?: unknown;\n cacheReadTokens?: unknown;\n thinkingTokens?: unknown;\n };\n}\n\n/** Coerce an unknown to a non-negative integer (0 on absence/garbage). */\nfunction toNonNegInt(v: unknown): number {\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.trunc(n));\n}\n\n/**\n * Normalize a model name from Droid's custom format (port of\n * droid.rs normalize_model_name). Order is load-bearing:\n * 1. strip a leading `custom:` prefix;\n * 2. remove every `[...]` bracket group (vendor tags like \"[Anthropic]\");\n * 3. trim trailing hyphens (NOT trailing digits — those are kept);\n * 4. lowercase;\n * 5. replace dots with hyphens;\n * 6. collapse runs of hyphens into one.\n */\nfunction normalizeModelName(model: string): string {\n let normalized = model.startsWith(\"custom:\") ? model.slice(\"custom:\".length) : model;\n\n // Remove [anything] groups (mirrors /\\[.*?\\]/g — bracket-depth-agnostic, just\n // drops chars while inside a bracket span).\n let stripped = \"\";\n let inBracket = false;\n for (const ch of normalized) {\n if (ch === \"[\") inBracket = true;\n else if (ch === \"]\") inBracket = false;\n else if (!inBracket) stripped += ch;\n }\n normalized = stripped;\n\n // Trim trailing hyphens only (mirrors /-+$/), keep trailing digits.\n normalized = normalized.replace(/-+$/, \"\");\n\n normalized = normalized.toLowerCase();\n normalized = normalized.replace(/\\./g, \"-\");\n\n // Collapse consecutive hyphens (mirrors /-+/g → \"-\").\n normalized = normalized.replace(/-+/g, \"-\");\n\n return normalized;\n}\n\n/**\n * Canonicalize a provider segment (port of the subset of\n * provider_identity.rs canonicalize_provider_segment needed by\n * get_default_model_from_provider). Returns the input lowercased/`-`→`_` when no\n * canonical alias applies, so the default-model switch can match cleanly.\n */\nfunction canonicalizeProviderSegment(segment: string): string | undefined {\n const normalized = segment.trim().replace(/\\/+$/, \"\").toLowerCase().replace(/-/g, \"_\");\n if (normalized.startsWith(\"<\") && normalized.endsWith(\">\")) return undefined;\n switch (normalized) {\n case \"\":\n case \"unknown\":\n return undefined;\n case \"x_ai\":\n case \"xai\":\n return \"xai\";\n case \"z_ai\":\n case \"zai\":\n return \"zai\";\n case \"moonshot\":\n case \"moonshotai\":\n return \"moonshotai\";\n case \"meta\":\n case \"meta_llama\":\n return \"meta_llama\";\n case \"azure\":\n case \"azure_ai\":\n return \"azure_ai\";\n case \"anthropic\":\n case \"vertex\":\n case \"vertex_ai\":\n return \"anthropic\";\n case \"together\":\n case \"together_ai\":\n return \"together_ai\";\n case \"fireworks\":\n case \"fireworks_ai\":\n return \"fireworks_ai\";\n case \"google\":\n case \"gemini\":\n return \"google\";\n case \"openai\":\n case \"openai_codex\":\n return \"openai\";\n case \"minimax\":\n case \"minimaxai\":\n case \"minimax_ai\":\n return \"minimax\";\n case \"mistral\":\n case \"mistralai\":\n return \"mistralai\";\n case \"ai21\":\n return \"ai21\";\n default:\n // Reject segments containing digits (model-name fragments, not providers).\n if (/[0-9]/.test(normalized)) return undefined;\n return normalized;\n }\n}\n\n/** Port of canonical_provider: first non-empty canonical tag, or the raw fallback. */\nfunction canonicalProvider(raw: string): string {\n for (const segment of raw.trim().replace(/\\/+$/, \"\").split(\"/\")) {\n const tag = canonicalizeProviderSegment(segment);\n if (tag !== undefined) return tag;\n if (segment.includes(\".\")) {\n for (const dotted of segment.split(\".\")) {\n const dottedTag = canonicalizeProviderSegment(dotted);\n if (dottedTag !== undefined) return dottedTag;\n }\n }\n }\n return raw;\n}\n\n/**\n * Provider-based default model when no model id is available (port of\n * get_default_model_from_provider).\n */\nfunction defaultModelFromProvider(provider: string): string {\n switch (canonicalProvider(provider)) {\n case \"anthropic\":\n return \"claude-unknown\";\n case \"openai\":\n return \"gpt-unknown\";\n case \"google\":\n return \"gemini-unknown\";\n case \"xai\":\n return \"grok-unknown\";\n default:\n return `${provider}-unknown`;\n }\n}\n\n/**\n * Try to extract a model name from the sibling .jsonl by scanning early lines\n * for a `Model:` system-reminder (port of extract_model_from_jsonl). The value\n * runs from after `Model:` up to the first `[`, `\\`, or `\"`, trimmed; the result\n * is run through normalizeModelName. Returns undefined when nothing matches.\n */\nfunction extractModelFromJsonl(jsonlPath: string): string | undefined {\n // readJsonlLines is fail-open ([] on a missing/unreadable file); we scan the\n // raw text of each parsed line's stringification is not what Rust does — Rust\n // scans raw file lines, so we read the lines as-is via the tolerant reader and\n // search their original text. Since readJsonlLines parses JSON, re-serialize is\n // lossy; instead search the parsed objects' stringified form, which preserves\n // the embedded \"Model:\" text that lives inside string fields.\n const lines = readJsonlLines(jsonlPath);\n const scan = lines.slice(0, JSONL_MODEL_SCAN_LIMIT);\n for (const obj of scan) {\n const text = typeof obj === \"string\" ? obj : JSON.stringify(obj);\n const pos = text.indexOf(\"Model:\");\n if (pos < 0) continue;\n const after = text.slice(pos + \"Model:\".length);\n let modelPart = \"\";\n for (const ch of after) {\n if (ch === \"[\" || ch === \"\\\\\" || ch === '\"') break;\n modelPart += ch;\n }\n const trimmed = modelPart.trim();\n if (trimmed !== \"\") return normalizeModelName(trimmed);\n }\n return undefined;\n}\n\n/** File mtime in epoch ms, or 0 when unreadable (matches the Rust mtime-or-0 path). */\nfunction fileMtimeOrZero(path: string): number {\n try {\n return Math.floor(statSync(path).mtimeMs);\n } catch {\n return 0;\n }\n}\n\n/** Session id from a settings filename: stem with a trailing `.settings` removed. */\nfunction sessionIdFromPath(path: string): string {\n const name = basename(path);\n // Drop a final extension (the `.json`), then strip `.settings`.\n const dot = name.lastIndexOf(\".\");\n const stem = dot > 0 ? name.slice(0, dot) : name;\n const id = stem.replace(/\\.settings$/, \"\");\n return id === \"\" ? \"unknown\" : id;\n}\n\n/** Parse one Droid settings.json file into usage records (port of parse_droid_file). */\nfunction parseDroidFile(path: string): UsageRecord[] {\n const raw = readJsonFile(path);\n if (typeof raw !== \"object\" || raw === null) return [];\n const settings = raw as DroidSettings;\n\n const usage = settings.tokenUsage;\n if (usage === undefined || usage === null || typeof usage !== \"object\") return [];\n\n const input = toNonNegInt(usage.inputTokens);\n const output = toNonNegInt(usage.outputTokens);\n const cacheWrite = toNonNegInt(usage.cacheCreationTokens);\n const cacheRead = toNonNegInt(usage.cacheReadTokens);\n const reasoning = toNonNegInt(usage.thinkingTokens);\n\n // Skip sessions with no usage at all.\n if (input + output + cacheWrite + cacheRead + reasoning === 0) return [];\n\n const sessionId = sessionIdFromPath(path);\n\n // Provider: providerLock if a non-empty string, else inferred from the model\n // (defaulting to \"unknown\" — matches the Rust unwrap_or(\"unknown\")).\n const providerLock =\n typeof settings.providerLock === \"string\" && settings.providerLock !== \"\"\n ? settings.providerLock\n : undefined;\n const modelField = typeof settings.model === \"string\" ? settings.model : undefined;\n const providerId =\n providerLock ?? inferProvider(modelField ?? \"\") ?? DEFAULT_PROVIDER;\n\n // Model: normalize the model field, else scan the sibling .jsonl, else default.\n let modelId: string;\n if (modelField !== undefined) {\n modelId = normalizeModelName(modelField);\n } else {\n const jsonlPath = path.replace(/\\.settings\\.json$/, \".jsonl\");\n modelId = jsonlPath !== path\n ? extractModelFromJsonl(jsonlPath) ?? defaultModelFromProvider(providerId)\n : defaultModelFromProvider(providerId);\n }\n\n // Timestamp: providerLockTimestamp (RFC3339 → ms) else file mtime; 0 → drop.\n let ts = 0;\n if (typeof settings.providerLockTimestamp === \"string\") {\n const parsed = Date.parse(settings.providerLockTimestamp);\n if (!Number.isNaN(parsed)) ts = parsed;\n }\n if (ts === 0) ts = fileMtimeOrZero(path);\n if (ts === 0) return [];\n\n const tokens = emptyTokens();\n tokens.input = input;\n tokens.output = output;\n tokens.cacheRead = cacheRead;\n tokens.cacheWrite = cacheWrite;\n tokens.reasoning = reasoning;\n\n return [\n {\n platformId: PLATFORM_ID,\n modelId,\n providerId,\n sessionId,\n tokens,\n ts,\n messageCount: 1,\n confidence: \"host-reported\",\n },\n ];\n}\n\n/** The Droid (Factory.ai) usage reader singleton. */\nconst droidReader: UsageReader = {\n platformId: PLATFORM_ID,\n kind: \"local\",\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\n const root = firstExistingRoot(PLATFORM_ID);\n if (root === undefined) return []; // no ~/.factory/sessions → fail-open\n\n // ~/.factory/sessions/*.settings.json\n const files = walkFiles(root, (name) => name.endsWith(\".settings.json\"));\n\n const records: UsageRecord[] = [];\n for (const file of files) {\n const rows = parseDroidFile(file);\n for (const row of rows) {\n if (sinceMs !== undefined && row.ts < sinceMs) continue;\n records.push(row);\n }\n }\n return records;\n },\n};\n\nexport default droidReader;\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
fileMtimeMs,
|
|
4
|
+
readJsonFile,
|
|
5
|
+
readJsonlLines
|
|
6
|
+
} from "./chunk-RK6VBLXP.js";
|
|
7
|
+
import {
|
|
8
|
+
firstExistingRoot,
|
|
9
|
+
walkFiles
|
|
10
|
+
} from "./chunk-JWNT5KW7.js";
|
|
11
|
+
import {
|
|
12
|
+
emptyTokens
|
|
13
|
+
} from "./chunk-XD7MY6HB.js";
|
|
14
|
+
|
|
15
|
+
// src/usage/readers/gemini-cli.ts
|
|
16
|
+
import { basename } from "path";
|
|
17
|
+
var PLATFORM_ID = "gemini-cli";
|
|
18
|
+
var PROVIDER = "google";
|
|
19
|
+
var DEFAULT_MODEL = "unknown";
|
|
20
|
+
function extractI64(v) {
|
|
21
|
+
if (typeof v === "number" && Number.isFinite(v)) return Math.trunc(v);
|
|
22
|
+
if (typeof v === "string") {
|
|
23
|
+
const n = Number(v);
|
|
24
|
+
if (Number.isInteger(n)) return n;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function extractString(v) {
|
|
29
|
+
return typeof v === "string" ? v : null;
|
|
30
|
+
}
|
|
31
|
+
function firstI64(obj, keys) {
|
|
32
|
+
for (const k of keys) {
|
|
33
|
+
const got = extractI64(obj[k]);
|
|
34
|
+
if (got !== null) return got;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function nonNeg(n) {
|
|
39
|
+
return n > 0 ? Math.trunc(n) : 0;
|
|
40
|
+
}
|
|
41
|
+
function satAdd(a, b) {
|
|
42
|
+
return a + b;
|
|
43
|
+
}
|
|
44
|
+
function parseTimestampValue(v) {
|
|
45
|
+
if (typeof v === "string") {
|
|
46
|
+
const ms = Date.parse(v);
|
|
47
|
+
if (!Number.isNaN(ms)) return ms;
|
|
48
|
+
const numeric = Number(v);
|
|
49
|
+
if (Number.isInteger(numeric)) {
|
|
50
|
+
if (numeric <= 0) return null;
|
|
51
|
+
return numeric >= 1e12 ? numeric : numeric * 1e3;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (typeof v === "number" && Number.isFinite(v)) {
|
|
56
|
+
const numeric = Math.trunc(v);
|
|
57
|
+
if (numeric <= 0) return null;
|
|
58
|
+
return numeric >= 1e12 ? numeric : numeric * 1e3;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
function extractTimestampFromValue(obj) {
|
|
63
|
+
const ts = obj.timestamp;
|
|
64
|
+
if (ts !== void 0) {
|
|
65
|
+
const got = parseTimestampValue(ts);
|
|
66
|
+
if (got !== null) return got;
|
|
67
|
+
}
|
|
68
|
+
const created = obj.created_at;
|
|
69
|
+
if (created !== void 0) {
|
|
70
|
+
const got = parseTimestampValue(created);
|
|
71
|
+
if (got !== null) return got;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
function parseRfc3339(v) {
|
|
76
|
+
if (typeof v !== "string") return null;
|
|
77
|
+
const ms = Date.parse(v);
|
|
78
|
+
return Number.isNaN(ms) ? null : ms;
|
|
79
|
+
}
|
|
80
|
+
function subtractCachedOverlap(input, cached) {
|
|
81
|
+
const i = nonNeg(input);
|
|
82
|
+
const c = nonNeg(cached);
|
|
83
|
+
const cachedPortion = Math.min(c, i);
|
|
84
|
+
return [i - cachedPortion, c];
|
|
85
|
+
}
|
|
86
|
+
function normalizeSessionInputAndCache(input, cached, output, reasoning, tool, total) {
|
|
87
|
+
const i = nonNeg(input);
|
|
88
|
+
const c = nonNeg(cached);
|
|
89
|
+
if (total === null) return [i, c];
|
|
90
|
+
const t = nonNeg(total);
|
|
91
|
+
const inclusiveTotal = satAdd(satAdd(satAdd(i, nonNeg(output)), nonNeg(reasoning)), nonNeg(tool));
|
|
92
|
+
const exclusiveTotal = satAdd(inclusiveTotal, c);
|
|
93
|
+
if (c > 0 && t === inclusiveTotal && t !== exclusiveTotal) {
|
|
94
|
+
return subtractCachedOverlap(i, c);
|
|
95
|
+
}
|
|
96
|
+
return [i, c];
|
|
97
|
+
}
|
|
98
|
+
function deserializeTokens(value) {
|
|
99
|
+
if (typeof value !== "object" || value === null) return null;
|
|
100
|
+
const obj = value;
|
|
101
|
+
return {
|
|
102
|
+
input: firstI64(obj, ["input", "prompt", "input_tokens", "prompt_tokens", "promptTokenCount"]),
|
|
103
|
+
output: firstI64(obj, [
|
|
104
|
+
"output",
|
|
105
|
+
"candidates",
|
|
106
|
+
"output_tokens",
|
|
107
|
+
"completion_tokens",
|
|
108
|
+
"candidatesTokenCount"
|
|
109
|
+
]),
|
|
110
|
+
cached: firstI64(obj, ["cached", "cached_tokens", "cachedContentTokenCount"]),
|
|
111
|
+
thoughts: firstI64(obj, ["thoughts", "reasoning", "thoughts_tokens"]),
|
|
112
|
+
tool: firstI64(obj, ["tool", "tool_tokens"]),
|
|
113
|
+
total: firstI64(obj, ["total", "totalTokenCount", "total_tokens"])
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function buildTokenRecord(model, sessionId, ts, tokens) {
|
|
117
|
+
const [input, cacheRead] = normalizeSessionInputAndCache(
|
|
118
|
+
tokens.input ?? 0,
|
|
119
|
+
tokens.cached ?? 0,
|
|
120
|
+
tokens.output ?? 0,
|
|
121
|
+
tokens.thoughts ?? 0,
|
|
122
|
+
tokens.tool ?? 0,
|
|
123
|
+
tokens.total
|
|
124
|
+
);
|
|
125
|
+
const tool = nonNeg(tokens.tool ?? 0);
|
|
126
|
+
const tk = emptyTokens();
|
|
127
|
+
tk.input = satAdd(input, tool);
|
|
128
|
+
tk.output = nonNeg(tokens.output ?? 0);
|
|
129
|
+
tk.cacheRead = cacheRead;
|
|
130
|
+
tk.cacheWrite = 0;
|
|
131
|
+
tk.reasoning = nonNeg(tokens.thoughts ?? 0);
|
|
132
|
+
return {
|
|
133
|
+
platformId: PLATFORM_ID,
|
|
134
|
+
modelId: model,
|
|
135
|
+
providerId: PROVIDER,
|
|
136
|
+
sessionId,
|
|
137
|
+
tokens: tk,
|
|
138
|
+
ts,
|
|
139
|
+
messageCount: 1,
|
|
140
|
+
confidence: "host-reported"
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function extractUsageFromValue(model, value) {
|
|
144
|
+
if (typeof value !== "object" || value === null) return null;
|
|
145
|
+
const outer = value;
|
|
146
|
+
const hasTokensWrapper = outer.tokens !== void 0;
|
|
147
|
+
const tokens = hasTokensWrapper && typeof outer.tokens === "object" && outer.tokens !== null ? outer.tokens : outer;
|
|
148
|
+
const promptInput = extractI64(tokens.prompt) ?? extractI64(tokens.input_tokens) ?? extractI64(tokens.prompt_tokens);
|
|
149
|
+
const netInput = extractI64(tokens.input);
|
|
150
|
+
const wrapperInput = hasTokensWrapper ? netInput : null;
|
|
151
|
+
const input = promptInput ?? wrapperInput ?? netInput ?? 0;
|
|
152
|
+
const output = extractI64(tokens.candidates) ?? extractI64(tokens.output) ?? extractI64(tokens.output_tokens) ?? extractI64(tokens.candidates_tokens) ?? 0;
|
|
153
|
+
const cached = extractI64(tokens.cached) ?? extractI64(tokens.cached_tokens) ?? 0;
|
|
154
|
+
const reasoning = extractI64(tokens.thoughts) ?? extractI64(tokens.thoughts_tokens) ?? extractI64(tokens.reasoning) ?? extractI64(tokens.reasoning_tokens) ?? 0;
|
|
155
|
+
if (input === 0 && output === 0 && cached === 0 && reasoning === 0) return null;
|
|
156
|
+
return {
|
|
157
|
+
model,
|
|
158
|
+
input,
|
|
159
|
+
output,
|
|
160
|
+
cached,
|
|
161
|
+
reasoning,
|
|
162
|
+
inputIncludesCache: promptInput !== null || wrapperInput !== null || netInput === null
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function extractUsages(stats, modelHint) {
|
|
166
|
+
const models = stats.models;
|
|
167
|
+
if (typeof models === "object" && models !== null && !Array.isArray(models)) {
|
|
168
|
+
const usages = [];
|
|
169
|
+
for (const [model, data] of Object.entries(models)) {
|
|
170
|
+
const usage = extractUsageFromValue(model, data);
|
|
171
|
+
if (usage !== null) usages.push(usage);
|
|
172
|
+
}
|
|
173
|
+
if (usages.length > 0) return usages;
|
|
174
|
+
}
|
|
175
|
+
const single = extractUsageFromValue(modelHint ?? DEFAULT_MODEL, stats);
|
|
176
|
+
return single === null ? [] : [single];
|
|
177
|
+
}
|
|
178
|
+
function buildRecordsFromStats(stats, modelHint, sessionId, ts) {
|
|
179
|
+
return extractUsages(stats, modelHint).map((usage) => {
|
|
180
|
+
const [input, cacheRead] = usage.inputIncludesCache ? subtractCachedOverlap(usage.input, usage.cached) : [nonNeg(usage.input), nonNeg(usage.cached)];
|
|
181
|
+
const tk = emptyTokens();
|
|
182
|
+
tk.input = input;
|
|
183
|
+
tk.output = nonNeg(usage.output);
|
|
184
|
+
tk.cacheRead = cacheRead;
|
|
185
|
+
tk.cacheWrite = 0;
|
|
186
|
+
tk.reasoning = nonNeg(usage.reasoning);
|
|
187
|
+
return {
|
|
188
|
+
platformId: PLATFORM_ID,
|
|
189
|
+
modelId: usage.model,
|
|
190
|
+
providerId: PROVIDER,
|
|
191
|
+
sessionId,
|
|
192
|
+
tokens: tk,
|
|
193
|
+
ts,
|
|
194
|
+
messageCount: 1,
|
|
195
|
+
confidence: "host-reported"
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function findStats(obj) {
|
|
200
|
+
if (typeof obj.stats === "object" && obj.stats !== null) return obj.stats;
|
|
201
|
+
const result = obj.result;
|
|
202
|
+
if (typeof result === "object" && result !== null) {
|
|
203
|
+
const inner = result.stats;
|
|
204
|
+
if (typeof inner === "object" && inner !== null) return inner;
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
function parseDirectTokenRecord(obj, modelHint, sessionId, fallbackTs) {
|
|
209
|
+
const model = extractString(obj.model) ?? modelHint;
|
|
210
|
+
if (model === null) return null;
|
|
211
|
+
if (obj.tokens === void 0) return null;
|
|
212
|
+
const tokens = deserializeTokens(obj.tokens);
|
|
213
|
+
if (tokens === null) return null;
|
|
214
|
+
const ts = extractTimestampFromValue(obj) ?? fallbackTs;
|
|
215
|
+
return buildTokenRecord(model, sessionId, ts, tokens);
|
|
216
|
+
}
|
|
217
|
+
function pathComponents(abs) {
|
|
218
|
+
return abs.split(/[\\/]+/).filter((c) => c !== "");
|
|
219
|
+
}
|
|
220
|
+
function isAcceptedFile(abs) {
|
|
221
|
+
const name = basename(abs);
|
|
222
|
+
if (name.startsWith("session-")) return true;
|
|
223
|
+
const comps = pathComponents(abs);
|
|
224
|
+
for (let i = 0; i + 1 < comps.length; i++) {
|
|
225
|
+
if (comps[i] === "tmp") {
|
|
226
|
+
const afterTmp = comps.slice(i + 1);
|
|
227
|
+
if (afterTmp.length === 3 && afterTmp[1] === "chats" && afterTmp[2] === name) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
function asGeminiSession(value) {
|
|
235
|
+
if (typeof value !== "object" || value === null) return null;
|
|
236
|
+
const obj = value;
|
|
237
|
+
if (!Array.isArray(obj.messages)) return null;
|
|
238
|
+
if (typeof obj.sessionId !== "string") return null;
|
|
239
|
+
return { sessionId: obj.sessionId, messages: obj.messages };
|
|
240
|
+
}
|
|
241
|
+
function parseSession(session, fallbackTs) {
|
|
242
|
+
const out = [];
|
|
243
|
+
for (const raw of session.messages) {
|
|
244
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
245
|
+
const msg = raw;
|
|
246
|
+
const tokens = deserializeTokens(msg.tokens);
|
|
247
|
+
if (tokens === null) continue;
|
|
248
|
+
const model = extractString(msg.model);
|
|
249
|
+
if (model === null) continue;
|
|
250
|
+
const ts = parseRfc3339(msg.timestamp) ?? fallbackTs;
|
|
251
|
+
out.push(buildTokenRecord(model, session.sessionId, ts, tokens));
|
|
252
|
+
}
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
255
|
+
function parseHeadlessValue(value, sessionId, fallbackTs) {
|
|
256
|
+
if (typeof value !== "object" || value === null) return [];
|
|
257
|
+
const obj = value;
|
|
258
|
+
if (obj.type === "gemini" || obj.tokens !== void 0) {
|
|
259
|
+
const direct = parseDirectTokenRecord(obj, null, sessionId, fallbackTs);
|
|
260
|
+
if (direct !== null) return [direct];
|
|
261
|
+
}
|
|
262
|
+
const stats = findStats(obj);
|
|
263
|
+
if (stats === null) return [];
|
|
264
|
+
const modelHint = extractString(obj.model);
|
|
265
|
+
const ts = extractTimestampFromValue(obj) ?? fallbackTs;
|
|
266
|
+
return buildRecordsFromStats(stats, modelHint, sessionId, ts);
|
|
267
|
+
}
|
|
268
|
+
function parseHeadlessJsonl(path, fallbackTs) {
|
|
269
|
+
const lines = readJsonlLines(path);
|
|
270
|
+
let sessionId = stripExt(basename(path)) || "unknown";
|
|
271
|
+
let currentModel = null;
|
|
272
|
+
const records = [];
|
|
273
|
+
const directIndexById = /* @__PURE__ */ new Map();
|
|
274
|
+
for (const raw of lines) {
|
|
275
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
276
|
+
const obj = raw;
|
|
277
|
+
const eventType = extractString(obj.type) ?? "";
|
|
278
|
+
if (eventType === "init") {
|
|
279
|
+
const model = extractString(obj.model);
|
|
280
|
+
if (model !== null) currentModel = model;
|
|
281
|
+
const id = extractString(obj.session_id) ?? extractString(obj.sessionId);
|
|
282
|
+
if (id !== null) sessionId = id;
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
const lineSession = extractString(obj.session_id) ?? extractString(obj.sessionId);
|
|
286
|
+
if (lineSession !== null) sessionId = lineSession;
|
|
287
|
+
if (eventType === "gemini" || obj.tokens !== void 0) {
|
|
288
|
+
const model = extractString(obj.model);
|
|
289
|
+
if (model !== null) currentModel = model;
|
|
290
|
+
const record = parseDirectTokenRecord(obj, currentModel, sessionId, fallbackTs);
|
|
291
|
+
if (record !== null) {
|
|
292
|
+
const id = extractString(obj.id);
|
|
293
|
+
if (id !== null) {
|
|
294
|
+
record.dedupKey = `${sessionId}:${id}`;
|
|
295
|
+
const existing = directIndexById.get(id);
|
|
296
|
+
if (existing !== void 0) {
|
|
297
|
+
records[existing] = record;
|
|
298
|
+
} else {
|
|
299
|
+
directIndexById.set(id, records.length);
|
|
300
|
+
records.push(record);
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
records.push(record);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
const stats = findStats(obj);
|
|
309
|
+
if (stats !== null) {
|
|
310
|
+
const ts = extractTimestampFromValue(obj) ?? fallbackTs;
|
|
311
|
+
for (const rec of buildRecordsFromStats(stats, currentModel, sessionId, ts)) {
|
|
312
|
+
records.push(rec);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return records;
|
|
317
|
+
}
|
|
318
|
+
function stripExt(name) {
|
|
319
|
+
const dot = name.lastIndexOf(".");
|
|
320
|
+
return dot > 0 ? name.slice(0, dot) : name;
|
|
321
|
+
}
|
|
322
|
+
function parseGeminiFile(path) {
|
|
323
|
+
const fallbackTs = fileMtimeMs(path);
|
|
324
|
+
if (path.endsWith(".jsonl")) {
|
|
325
|
+
return parseHeadlessJsonl(path, fallbackTs);
|
|
326
|
+
}
|
|
327
|
+
const value = readJsonFile(path);
|
|
328
|
+
if (value !== void 0) {
|
|
329
|
+
const session = asGeminiSession(value);
|
|
330
|
+
if (session !== null) {
|
|
331
|
+
return parseSession(session, fallbackTs);
|
|
332
|
+
}
|
|
333
|
+
const sessionId = stripExt(basename(path)) || "unknown";
|
|
334
|
+
const headless = parseHeadlessValue(value, sessionId, fallbackTs);
|
|
335
|
+
if (headless.length > 0) return headless;
|
|
336
|
+
}
|
|
337
|
+
return parseHeadlessJsonl(path, fallbackTs);
|
|
338
|
+
}
|
|
339
|
+
var geminiReader = {
|
|
340
|
+
platformId: PLATFORM_ID,
|
|
341
|
+
kind: "local",
|
|
342
|
+
async read({ sinceMs }) {
|
|
343
|
+
const root = firstExistingRoot(PLATFORM_ID);
|
|
344
|
+
if (root === void 0) return [];
|
|
345
|
+
const files = walkFiles(root, (name, abs) => {
|
|
346
|
+
if (!name.endsWith(".json") && !name.endsWith(".jsonl")) return false;
|
|
347
|
+
return isAcceptedFile(abs);
|
|
348
|
+
});
|
|
349
|
+
const records = [];
|
|
350
|
+
for (const file of files) {
|
|
351
|
+
let rows;
|
|
352
|
+
try {
|
|
353
|
+
rows = parseGeminiFile(file);
|
|
354
|
+
} catch {
|
|
355
|
+
rows = [];
|
|
356
|
+
}
|
|
357
|
+
for (const row of rows) {
|
|
358
|
+
if (sinceMs !== void 0 && row.ts < sinceMs) continue;
|
|
359
|
+
records.push(row);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return records;
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
var gemini_cli_default = geminiReader;
|
|
366
|
+
export {
|
|
367
|
+
gemini_cli_default as default
|
|
368
|
+
};
|
|
369
|
+
//# sourceMappingURL=gemini-cli-CIRW37TU.js.map
|