agentlytics 0.1.16 → 0.1.18
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/editors/codex.js +4 -2
- package/editors/opencode.js +168 -18
- package/package.json +1 -1
- package/pricing.json +123 -1
package/editors/codex.js
CHANGED
|
@@ -376,9 +376,11 @@ function subtractRawUsage(current, previous) {
|
|
|
376
376
|
}
|
|
377
377
|
|
|
378
378
|
function convertToDelta(raw) {
|
|
379
|
+
const cacheRead = Math.min(raw.cached_input_tokens, raw.input_tokens);
|
|
380
|
+
const billableInput = Math.max(raw.input_tokens - cacheRead, 0);
|
|
379
381
|
return {
|
|
380
|
-
inputTokens:
|
|
381
|
-
cacheRead
|
|
382
|
+
inputTokens: billableInput,
|
|
383
|
+
cacheRead,
|
|
382
384
|
outputTokens: raw.output_tokens,
|
|
383
385
|
totalTokens: raw.total_tokens > 0 ? raw.total_tokens : raw.input_tokens + raw.output_tokens,
|
|
384
386
|
};
|
package/editors/opencode.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const os = require('os');
|
|
4
|
+
const Database = require('better-sqlite3');
|
|
4
5
|
|
|
5
6
|
// OpenCode stores data in different locations depending on the platform
|
|
6
7
|
// - Windows: %LOCALAPPDATA%\opencode\storage (not Roaming)
|
|
@@ -22,6 +23,118 @@ const SESSION_DIR = path.join(STORAGE_DIR, 'session');
|
|
|
22
23
|
const MESSAGE_DIR = path.join(STORAGE_DIR, 'message');
|
|
23
24
|
const PART_DIR = path.join(STORAGE_DIR, 'part');
|
|
24
25
|
|
|
26
|
+
// OpenCode also stores data in a SQLite database (older/primary store)
|
|
27
|
+
function getOpenCodeDbPath() {
|
|
28
|
+
const home = os.homedir();
|
|
29
|
+
switch (process.platform) {
|
|
30
|
+
case 'win32':
|
|
31
|
+
return path.join(home, 'AppData', 'Local', 'opencode', 'opencode.db');
|
|
32
|
+
case 'darwin':
|
|
33
|
+
case 'linux':
|
|
34
|
+
default:
|
|
35
|
+
return path.join(home, '.local', 'share', 'opencode', 'opencode.db');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const DB_PATH = getOpenCodeDbPath();
|
|
40
|
+
|
|
41
|
+
// ============================================================
|
|
42
|
+
// Query SQLite using better-sqlite3
|
|
43
|
+
// ============================================================
|
|
44
|
+
|
|
45
|
+
function queryDb(sql) {
|
|
46
|
+
if (!fs.existsSync(DB_PATH)) return [];
|
|
47
|
+
try {
|
|
48
|
+
const db = new Database(DB_PATH, { readonly: true });
|
|
49
|
+
const rows = db.prepare(sql).all();
|
|
50
|
+
db.close();
|
|
51
|
+
return rows;
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getSqliteSessions() {
|
|
58
|
+
return queryDb(
|
|
59
|
+
`SELECT s.id, s.title, s.directory, s.time_created, s.time_updated,
|
|
60
|
+
p.worktree, p.name as project_name,
|
|
61
|
+
(SELECT count(*) FROM message m WHERE m.session_id = s.id) as msg_count
|
|
62
|
+
FROM session s LEFT JOIN project p ON s.project_id = p.id
|
|
63
|
+
ORDER BY s.time_updated DESC`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getSqliteMessages(sessionId) {
|
|
68
|
+
if (!fs.existsSync(DB_PATH)) return [];
|
|
69
|
+
try {
|
|
70
|
+
const db = new Database(DB_PATH, { readonly: true });
|
|
71
|
+
const messages = db.prepare(
|
|
72
|
+
`SELECT m.id as msg_id, m.data as msg_data, m.time_created
|
|
73
|
+
FROM message m WHERE m.session_id = ? ORDER BY m.time_created ASC`
|
|
74
|
+
).all(sessionId);
|
|
75
|
+
|
|
76
|
+
const result = [];
|
|
77
|
+
for (const msg of messages) {
|
|
78
|
+
let msgData;
|
|
79
|
+
try { msgData = JSON.parse(msg.msg_data); } catch { continue; }
|
|
80
|
+
|
|
81
|
+
const role = msgData.role;
|
|
82
|
+
if (!role) continue;
|
|
83
|
+
|
|
84
|
+
const parts = db.prepare(
|
|
85
|
+
`SELECT data FROM part WHERE message_id = ? ORDER BY time_created ASC`
|
|
86
|
+
).all(msg.msg_id);
|
|
87
|
+
|
|
88
|
+
const contentParts = [];
|
|
89
|
+
for (const part of parts) {
|
|
90
|
+
let partData;
|
|
91
|
+
try { partData = JSON.parse(part.data); } catch { continue; }
|
|
92
|
+
const type = partData.type;
|
|
93
|
+
|
|
94
|
+
if (type === 'text' && partData.text) {
|
|
95
|
+
contentParts.push(partData.text);
|
|
96
|
+
} else if (type === 'thinking' || type === 'reasoning') {
|
|
97
|
+
if (partData.text) contentParts.push(`[thinking] ${partData.text}`);
|
|
98
|
+
} else if (type === 'tool-call' || type === 'tool_use' || type === 'tool-use' || type === 'tool') {
|
|
99
|
+
const toolName = partData.name || partData.toolName || partData.tool || 'tool';
|
|
100
|
+
let argKeys = '';
|
|
101
|
+
try {
|
|
102
|
+
const input = typeof partData.input === 'string' ? JSON.parse(partData.input) : (partData.input || partData.args || partData.arguments || partData.state?.input || {});
|
|
103
|
+
argKeys = typeof input === 'object' ? Object.keys(input).join(', ') : '';
|
|
104
|
+
} catch {}
|
|
105
|
+
contentParts.push(`[tool-call: ${toolName}(${argKeys})]`);
|
|
106
|
+
} else if (type === 'tool-result' || type === 'tool_result') {
|
|
107
|
+
const preview = (partData.text || partData.output || partData.state?.output || '').substring(0, 500);
|
|
108
|
+
contentParts.push(`[tool-result] ${preview}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const content = contentParts.join('\n');
|
|
113
|
+
if (!content) continue;
|
|
114
|
+
|
|
115
|
+
let modelValue = null;
|
|
116
|
+
if (typeof msgData.modelID === 'string') {
|
|
117
|
+
modelValue = msgData.modelID;
|
|
118
|
+
} else if (msgData.model && typeof msgData.model === 'object' && msgData.model.modelID) {
|
|
119
|
+
modelValue = msgData.model.modelID;
|
|
120
|
+
} else if (typeof msgData.model === 'string') {
|
|
121
|
+
modelValue = msgData.model;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
result.push({
|
|
125
|
+
role: role === 'user' ? 'user' : role === 'assistant' ? 'assistant' : role,
|
|
126
|
+
content,
|
|
127
|
+
_model: modelValue,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
db.close();
|
|
132
|
+
return result;
|
|
133
|
+
} catch {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
25
138
|
// ============================================================
|
|
26
139
|
// Scan JSON files from OpenCode storage
|
|
27
140
|
// ============================================================
|
|
@@ -156,27 +269,64 @@ function getMessagesForSession(sessionId) {
|
|
|
156
269
|
const name = 'opencode';
|
|
157
270
|
|
|
158
271
|
function getChats() {
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
272
|
+
const seen = new Set();
|
|
273
|
+
const chats = [];
|
|
274
|
+
|
|
275
|
+
// 1. JSON file-based sessions (newer storage format)
|
|
276
|
+
const fileSessions = getAllSessions();
|
|
277
|
+
for (const s of fileSessions) {
|
|
278
|
+
seen.add(s.id);
|
|
279
|
+
chats.push({
|
|
280
|
+
source: 'opencode',
|
|
281
|
+
composerId: s.id,
|
|
282
|
+
name: s.title || null,
|
|
283
|
+
createdAt: s.time?.created || null,
|
|
284
|
+
lastUpdatedAt: s.time?.updated || null,
|
|
285
|
+
mode: s.mode || 'opencode',
|
|
286
|
+
folder: s.directory || null,
|
|
287
|
+
encrypted: false,
|
|
288
|
+
bubbleCount: getMessageCount(s.id),
|
|
289
|
+
_agent: s.agent,
|
|
290
|
+
_model: s.modelID,
|
|
291
|
+
_provider: s.providerID,
|
|
292
|
+
_sessionData: s,
|
|
293
|
+
_storageType: 'file',
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 2. SQLite sessions (older/primary store) — add any not already found in files
|
|
298
|
+
const dbSessions = getSqliteSessions();
|
|
299
|
+
for (const row of dbSessions) {
|
|
300
|
+
if (seen.has(row.id)) continue;
|
|
301
|
+
seen.add(row.id);
|
|
302
|
+
chats.push({
|
|
303
|
+
source: 'opencode',
|
|
304
|
+
composerId: row.id,
|
|
305
|
+
name: cleanTitle(row.title),
|
|
306
|
+
createdAt: row.time_created || null,
|
|
307
|
+
lastUpdatedAt: row.time_updated || null,
|
|
308
|
+
mode: 'opencode',
|
|
309
|
+
folder: row.worktree || row.directory || null,
|
|
310
|
+
encrypted: false,
|
|
311
|
+
bubbleCount: row.msg_count || 0,
|
|
312
|
+
_storageType: 'sqlite',
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return chats.sort((a, b) => (b.lastUpdatedAt || 0) - (a.lastUpdatedAt || 0));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function cleanTitle(title) {
|
|
320
|
+
if (!title) return null;
|
|
321
|
+
if (title.startsWith('New session - ')) return null;
|
|
322
|
+
return title.substring(0, 120) || null;
|
|
176
323
|
}
|
|
177
324
|
|
|
178
325
|
function getMessages(chat) {
|
|
179
|
-
|
|
326
|
+
// Prefer file-based messages; fall back to SQLite
|
|
327
|
+
const fileMessages = getMessagesForSession(chat.composerId);
|
|
328
|
+
if (fileMessages.length > 0) return fileMessages;
|
|
329
|
+
return getSqliteMessages(chat.composerId);
|
|
180
330
|
}
|
|
181
331
|
|
|
182
332
|
const labels = { 'opencode': 'OpenCode' };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlytics",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "Comprehensive analytics dashboard for AI coding agents — Cursor, Windsurf, Claude Code, VS Code Copilot, Zed, Antigravity, OpenCode, Command Code",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
package/pricing.json
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
"OpenAI": "developers.openai.com/api/docs/pricing",
|
|
7
7
|
"Google": "ai.google.dev/gemini-api/docs/pricing",
|
|
8
8
|
"xAI": "docs.x.ai/developers/models",
|
|
9
|
-
"DeepSeek": "api-docs.deepseek.com/quick_start/pricing"
|
|
9
|
+
"DeepSeek": "api-docs.deepseek.com/quick_start/pricing",
|
|
10
|
+
"Z.ai": "docs.z.ai/guides/overview/pricing",
|
|
11
|
+
"Zen": "opencode.ai/docs/zen/"
|
|
10
12
|
},
|
|
11
13
|
"lastVerified": "2026-03",
|
|
12
14
|
"notes": {
|
|
@@ -818,5 +820,125 @@
|
|
|
818
820
|
"output": 25,
|
|
819
821
|
"cacheRead": 1.25,
|
|
820
822
|
"cacheWrite": 0
|
|
823
|
+
},
|
|
824
|
+
"glm-5": {
|
|
825
|
+
"input": 1,
|
|
826
|
+
"output": 3.2,
|
|
827
|
+
"cacheRead": 0.2,
|
|
828
|
+
"cacheWrite": 0
|
|
829
|
+
},
|
|
830
|
+
"glm-5-code": {
|
|
831
|
+
"input": 1.2,
|
|
832
|
+
"output": 5,
|
|
833
|
+
"cacheRead": 0.3,
|
|
834
|
+
"cacheWrite": 0
|
|
835
|
+
},
|
|
836
|
+
"glm-4.7": {
|
|
837
|
+
"input": 0.6,
|
|
838
|
+
"output": 2.2,
|
|
839
|
+
"cacheRead": 0.11,
|
|
840
|
+
"cacheWrite": 0
|
|
841
|
+
},
|
|
842
|
+
"glm-4.7-flashx": {
|
|
843
|
+
"input": 0.07,
|
|
844
|
+
"output": 0.4,
|
|
845
|
+
"cacheRead": 0.01,
|
|
846
|
+
"cacheWrite": 0
|
|
847
|
+
},
|
|
848
|
+
"glm-4.6": {
|
|
849
|
+
"input": 0.6,
|
|
850
|
+
"output": 2.2,
|
|
851
|
+
"cacheRead": 0.11,
|
|
852
|
+
"cacheWrite": 0
|
|
853
|
+
},
|
|
854
|
+
"glm-4.5": {
|
|
855
|
+
"input": 0.6,
|
|
856
|
+
"output": 2.2,
|
|
857
|
+
"cacheRead": 0.11,
|
|
858
|
+
"cacheWrite": 0
|
|
859
|
+
},
|
|
860
|
+
"glm-4.5-x": {
|
|
861
|
+
"input": 2.2,
|
|
862
|
+
"output": 8.9,
|
|
863
|
+
"cacheRead": 0.45,
|
|
864
|
+
"cacheWrite": 0
|
|
865
|
+
},
|
|
866
|
+
"glm-4.5-air": {
|
|
867
|
+
"input": 0.2,
|
|
868
|
+
"output": 1.1,
|
|
869
|
+
"cacheRead": 0.03,
|
|
870
|
+
"cacheWrite": 0
|
|
871
|
+
},
|
|
872
|
+
"glm-4.5-airx": {
|
|
873
|
+
"input": 1.1,
|
|
874
|
+
"output": 4.5,
|
|
875
|
+
"cacheRead": 0.22,
|
|
876
|
+
"cacheWrite": 0
|
|
877
|
+
},
|
|
878
|
+
"glm-4-32b-0414-128k": {
|
|
879
|
+
"input": 0.1,
|
|
880
|
+
"output": 0.1,
|
|
881
|
+
"cacheRead": 0,
|
|
882
|
+
"cacheWrite": 0
|
|
883
|
+
},
|
|
884
|
+
"glm-4.7-flash": {
|
|
885
|
+
"input": 0,
|
|
886
|
+
"output": 0,
|
|
887
|
+
"cacheRead": 0,
|
|
888
|
+
"cacheWrite": 0
|
|
889
|
+
},
|
|
890
|
+
"glm-4.5-flash": {
|
|
891
|
+
"input": 0,
|
|
892
|
+
"output": 0,
|
|
893
|
+
"cacheRead": 0,
|
|
894
|
+
"cacheWrite": 0
|
|
895
|
+
},
|
|
896
|
+
"big-pickle": {
|
|
897
|
+
"input": 0,
|
|
898
|
+
"output": 0,
|
|
899
|
+
"cacheRead": 0,
|
|
900
|
+
"cacheWrite": 0
|
|
901
|
+
},
|
|
902
|
+
"minimax-m2.5-free": {
|
|
903
|
+
"input": 0,
|
|
904
|
+
"output": 0,
|
|
905
|
+
"cacheRead": 0,
|
|
906
|
+
"cacheWrite": 0
|
|
907
|
+
},
|
|
908
|
+
"minimax-m2.5": {
|
|
909
|
+
"input": 0.3,
|
|
910
|
+
"output": 1.2,
|
|
911
|
+
"cacheRead": 0.06,
|
|
912
|
+
"cacheWrite": 0.375
|
|
913
|
+
},
|
|
914
|
+
"minimax-m2.1": {
|
|
915
|
+
"input": 0.3,
|
|
916
|
+
"output": 1.2,
|
|
917
|
+
"cacheRead": 0.1,
|
|
918
|
+
"cacheWrite": 0
|
|
919
|
+
},
|
|
920
|
+
"kimi-k2.5": {
|
|
921
|
+
"input": 0.6,
|
|
922
|
+
"output": 3,
|
|
923
|
+
"cacheRead": 0.1,
|
|
924
|
+
"cacheWrite": 0
|
|
925
|
+
},
|
|
926
|
+
"kimi-k2-thinking": {
|
|
927
|
+
"input": 0.4,
|
|
928
|
+
"output": 2.5,
|
|
929
|
+
"cacheRead": 0,
|
|
930
|
+
"cacheWrite": 0
|
|
931
|
+
},
|
|
932
|
+
"kimi-k2": {
|
|
933
|
+
"input": 0.4,
|
|
934
|
+
"output": 2.5,
|
|
935
|
+
"cacheRead": 0,
|
|
936
|
+
"cacheWrite": 0
|
|
937
|
+
},
|
|
938
|
+
"qwen3-coder-480b": {
|
|
939
|
+
"input": 0.45,
|
|
940
|
+
"output": 1.5,
|
|
941
|
+
"cacheRead": 0,
|
|
942
|
+
"cacheWrite": 0
|
|
821
943
|
}
|
|
822
944
|
}
|