opencode-usage-total 0.1.2 → 0.1.3
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/{chunk-XNCJ32GU.js → chunk-2K4AVHCL.js} +16 -11
- package/dist/index.js +55 -22
- package/dist/usage-total.js +1 -1
- package/package.json +5 -2
|
@@ -1,31 +1,36 @@
|
|
|
1
1
|
// usage-total.ts
|
|
2
2
|
var UsageTotalPlugin = async ({ client }) => {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
try {
|
|
4
|
+
await client.app.log({
|
|
5
|
+
body: {
|
|
6
|
+
service: "usage-total",
|
|
7
|
+
level: "info",
|
|
8
|
+
message: "usage-total plugin loaded"
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
} catch {
|
|
12
|
+
}
|
|
10
13
|
return {
|
|
11
14
|
event: async ({ event }) => {
|
|
12
15
|
if (event.type === "session.created") {
|
|
13
|
-
|
|
16
|
+
client.app.log({
|
|
14
17
|
body: {
|
|
15
18
|
service: "usage-total",
|
|
16
|
-
level: "
|
|
19
|
+
level: "debug",
|
|
17
20
|
message: `session.created [${event.properties.info.id}]`
|
|
18
21
|
}
|
|
22
|
+
}).catch(() => {
|
|
19
23
|
});
|
|
20
24
|
}
|
|
21
25
|
if (event.type === "message.updated") {
|
|
22
26
|
const msg = event.properties.info;
|
|
23
|
-
|
|
27
|
+
client.app.log({
|
|
24
28
|
body: {
|
|
25
29
|
service: "usage-total",
|
|
26
|
-
level: "
|
|
30
|
+
level: "debug",
|
|
27
31
|
message: `message.updated [${msg.id}] role=${msg.role}`
|
|
28
32
|
}
|
|
33
|
+
}).catch(() => {
|
|
29
34
|
});
|
|
30
35
|
}
|
|
31
36
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,28 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
UsageTotalPlugin
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2K4AVHCL.js";
|
|
4
4
|
|
|
5
5
|
// usage-total-tui.tsx
|
|
6
6
|
import { createRoot, createSignal } from "solid-js";
|
|
7
7
|
|
|
8
8
|
// package.json
|
|
9
|
-
var version = "0.1.
|
|
9
|
+
var version = "0.1.3";
|
|
10
10
|
|
|
11
|
-
//
|
|
12
|
-
import { jsx, jsxs } from "@opentui/solid/jsx-runtime";
|
|
13
|
-
var DEFAULT_AGENT = "primary";
|
|
14
|
-
var UNKNOWN_ID = "?";
|
|
15
|
-
function resolveRouteSessionID(api) {
|
|
16
|
-
const current = api.route.current;
|
|
17
|
-
if (current.name === "session") {
|
|
18
|
-
const sid = current.params?.sessionID;
|
|
19
|
-
return typeof sid === "string" ? sid : void 0;
|
|
20
|
-
}
|
|
21
|
-
return void 0;
|
|
22
|
-
}
|
|
23
|
-
function modelTokens(m) {
|
|
24
|
-
return m.tokensInput + m.tokensOutput + m.tokensReasoning;
|
|
25
|
-
}
|
|
11
|
+
// helpers.ts
|
|
26
12
|
function safeNum(value) {
|
|
27
13
|
const n = typeof value === "number" ? value : Number(value);
|
|
28
14
|
return Number.isFinite(n) ? n : 0;
|
|
@@ -31,17 +17,36 @@ function roundCost(n) {
|
|
|
31
17
|
return safeNum(Number(n.toFixed(6)));
|
|
32
18
|
}
|
|
33
19
|
function fmtTokens(n) {
|
|
20
|
+
if (n < 0) return "0";
|
|
34
21
|
if (!Number.isFinite(n) || n === 0) return "0";
|
|
35
22
|
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
36
23
|
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
37
24
|
return String(Math.round(n));
|
|
38
25
|
}
|
|
39
26
|
function fmtCost(n) {
|
|
27
|
+
if (n < 0) return "";
|
|
40
28
|
if (!Number.isFinite(n) || n === 0) return "";
|
|
41
29
|
if (n < 0.01) return `$${n.toFixed(4)}`;
|
|
42
30
|
return `$${n.toFixed(2)}`;
|
|
43
31
|
}
|
|
32
|
+
function modelTokens(m) {
|
|
33
|
+
return m.tokensInput + m.tokensOutput + m.tokensReasoning + m.tokensCacheRead + m.tokensCacheWrite;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// usage-total-tui.tsx
|
|
37
|
+
import { jsx, jsxs } from "@opentui/solid/jsx-runtime";
|
|
38
|
+
var DEFAULT_AGENT = "primary";
|
|
39
|
+
var UNKNOWN_ID = "?";
|
|
40
|
+
function resolveRouteSessionID(api) {
|
|
41
|
+
const current = api.route.current;
|
|
42
|
+
if (current.name === "session") {
|
|
43
|
+
const sid = current.params?.sessionID;
|
|
44
|
+
return typeof sid === "string" ? sid : void 0;
|
|
45
|
+
}
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
44
48
|
var initialized = false;
|
|
49
|
+
var lastToastTime = 0;
|
|
45
50
|
var tui = async (api) => {
|
|
46
51
|
if (initialized) {
|
|
47
52
|
api.ui?.toast?.({
|
|
@@ -133,6 +138,15 @@ var tui = async (api) => {
|
|
|
133
138
|
loadedSessions.add(sessionID);
|
|
134
139
|
const saved = api.kv?.get?.(kvKey(sessionID));
|
|
135
140
|
if (saved && saved.length > 0) {
|
|
141
|
+
const valid = Array.isArray(saved) && typeof saved[0].provider === "string" && typeof saved[0].model === "string" && typeof saved[0].agent === "string";
|
|
142
|
+
if (!valid) {
|
|
143
|
+
api.ui?.toast?.({
|
|
144
|
+
message: "usage-total: discarded corrupt saved model data",
|
|
145
|
+
variant: "warning"
|
|
146
|
+
});
|
|
147
|
+
api.kv?.set?.(kvKey(sessionID), void 0);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
136
150
|
setModelState((current) => ({ ...current, [sessionID]: saved }));
|
|
137
151
|
}
|
|
138
152
|
}
|
|
@@ -147,6 +161,8 @@ var tui = async (api) => {
|
|
|
147
161
|
const safeInput = safeNum(tokens.input);
|
|
148
162
|
const safeOutput = safeNum(tokens.output);
|
|
149
163
|
const safeReasoning = safeNum(tokens.reasoning);
|
|
164
|
+
const safeCacheRead = safeNum(tokens.cacheRead);
|
|
165
|
+
const safeCacheWrite = safeNum(tokens.cacheWrite);
|
|
150
166
|
if (existingIdx >= 0) {
|
|
151
167
|
const existing = sessionModels[existingIdx];
|
|
152
168
|
sessionModels[existingIdx] = {
|
|
@@ -155,6 +171,10 @@ var tui = async (api) => {
|
|
|
155
171
|
tokensInput: safeNum(existing.tokensInput + safeInput),
|
|
156
172
|
tokensOutput: safeNum(existing.tokensOutput + safeOutput),
|
|
157
173
|
tokensReasoning: safeNum(existing.tokensReasoning + safeReasoning),
|
|
174
|
+
tokensCacheRead: safeNum(existing.tokensCacheRead + safeCacheRead),
|
|
175
|
+
tokensCacheWrite: safeNum(
|
|
176
|
+
existing.tokensCacheWrite + safeCacheWrite
|
|
177
|
+
),
|
|
158
178
|
messageCount: existing.messageCount + 1
|
|
159
179
|
};
|
|
160
180
|
} else {
|
|
@@ -164,6 +184,8 @@ var tui = async (api) => {
|
|
|
164
184
|
tokensInput: safeInput,
|
|
165
185
|
tokensOutput: safeOutput,
|
|
166
186
|
tokensReasoning: safeReasoning,
|
|
187
|
+
tokensCacheRead: safeCacheRead,
|
|
188
|
+
tokensCacheWrite: safeCacheWrite,
|
|
167
189
|
messageCount: 1
|
|
168
190
|
});
|
|
169
191
|
}
|
|
@@ -176,15 +198,24 @@ var tui = async (api) => {
|
|
|
176
198
|
}
|
|
177
199
|
function trackModel(eventSessionID, entry, cost, tokens) {
|
|
178
200
|
const isNew = upsertModel(eventSessionID, entry, cost, tokens);
|
|
179
|
-
if (isNew) {
|
|
201
|
+
if (isNew && Date.now() - lastToastTime > 2e3) {
|
|
202
|
+
lastToastTime = Date.now();
|
|
180
203
|
api.ui?.toast?.({
|
|
181
204
|
message: `${entry.agent}: ${entry.model}`,
|
|
182
205
|
variant: "success"
|
|
183
206
|
});
|
|
184
207
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
208
|
+
let cursor = eventSessionID;
|
|
209
|
+
const visited = /* @__PURE__ */ new Set();
|
|
210
|
+
while (true) {
|
|
211
|
+
visited.add(cursor);
|
|
212
|
+
const sess = api.state?.session?.get?.(cursor);
|
|
213
|
+
if (!sess?.parentID || sess.parentID === cursor || visited.has(sess.parentID))
|
|
214
|
+
break;
|
|
215
|
+
cursor = sess.parentID;
|
|
216
|
+
}
|
|
217
|
+
if (cursor !== eventSessionID) {
|
|
218
|
+
upsertModel(cursor, entry, cost, tokens);
|
|
188
219
|
}
|
|
189
220
|
}
|
|
190
221
|
unsub = api.event?.on?.("message.updated", (event) => {
|
|
@@ -210,7 +241,9 @@ var tui = async (api) => {
|
|
|
210
241
|
tokens = {
|
|
211
242
|
input: safeNum(info.tokens?.input),
|
|
212
243
|
output: safeNum(info.tokens?.output),
|
|
213
|
-
reasoning: safeNum(info.tokens?.reasoning)
|
|
244
|
+
reasoning: safeNum(info.tokens?.reasoning),
|
|
245
|
+
cacheRead: safeNum(info.tokens?.cache?.read),
|
|
246
|
+
cacheWrite: safeNum(info.tokens?.cache?.write)
|
|
214
247
|
};
|
|
215
248
|
} else {
|
|
216
249
|
return;
|
package/dist/usage-total.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-usage-total",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Track model usage, tokens, and costs per agent in the OpenCode TUI sidebar",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "tsup",
|
|
33
|
+
"test": "vitest",
|
|
34
|
+
"test:run": "vitest run",
|
|
33
35
|
"prepublishOnly": "npm run build"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
@@ -38,6 +40,7 @@
|
|
|
38
40
|
"@opentui/keymap": "^0.4.1",
|
|
39
41
|
"@opentui/solid": "^0.4.1",
|
|
40
42
|
"solid-js": "^1.9.12",
|
|
41
|
-
"tsup": "^8.5.1"
|
|
43
|
+
"tsup": "^8.5.1",
|
|
44
|
+
"vitest": "^4.1.9"
|
|
42
45
|
}
|
|
43
46
|
}
|