agentlytics 0.2.7 → 0.2.9
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/README.md +60 -1
- package/cache.js +95 -170
- package/deno.json +9 -0
- package/editors/opencode.js +55 -28
- package/index.js +46 -8
- package/mod.ts +1020 -0
- package/package.json +7 -2
- package/relay-server.js +1 -1
- package/ui/package.json +1 -1
- package/ui/src/App.jsx +13 -8
- package/ui/src/hooks/useLive.jsx +15 -0
- package/ui/src/pages/Dashboard.jsx +3 -2
- package/ui/src/pages/DeepAnalysis.jsx +10 -7
package/editors/opencode.js
CHANGED
|
@@ -36,6 +36,38 @@ function queryDb(sql) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function extractModelInfo(data) {
|
|
40
|
+
let modelValue = null;
|
|
41
|
+
let providerValue = null;
|
|
42
|
+
|
|
43
|
+
if (typeof data?.modelID === 'string') {
|
|
44
|
+
modelValue = data.modelID;
|
|
45
|
+
providerValue = typeof data.providerID === 'string' ? data.providerID : null;
|
|
46
|
+
} else if (data?.model && typeof data.model === 'object') {
|
|
47
|
+
modelValue = typeof data.model.modelID === 'string' ? data.model.modelID : null;
|
|
48
|
+
providerValue = typeof data.providerID === 'string'
|
|
49
|
+
? data.providerID
|
|
50
|
+
: (typeof data.model.providerID === 'string' ? data.model.providerID : null);
|
|
51
|
+
} else if (typeof data?.model === 'string') {
|
|
52
|
+
modelValue = data.model;
|
|
53
|
+
providerValue = typeof data.providerID === 'string' ? data.providerID : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { modelValue, providerValue };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function extractTokenInfo(data) {
|
|
60
|
+
const tokens = data?.tokens && typeof data.tokens === 'object' ? data.tokens : null;
|
|
61
|
+
const cache = tokens?.cache && typeof tokens.cache === 'object' ? tokens.cache : null;
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
inputTokens: tokens?.input,
|
|
65
|
+
outputTokens: tokens?.output,
|
|
66
|
+
cacheRead: cache?.read,
|
|
67
|
+
cacheWrite: cache?.write,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
39
71
|
function getSqliteSessions() {
|
|
40
72
|
return queryDb(
|
|
41
73
|
`SELECT s.id, s.title, s.directory, s.time_created, s.time_updated,
|
|
@@ -94,19 +126,18 @@ function getSqliteMessages(sessionId) {
|
|
|
94
126
|
const content = contentParts.join('\n');
|
|
95
127
|
if (!content) continue;
|
|
96
128
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
modelValue = msgData.modelID;
|
|
100
|
-
} else if (msgData.model && typeof msgData.model === 'object' && msgData.model.modelID) {
|
|
101
|
-
modelValue = msgData.model.modelID;
|
|
102
|
-
} else if (typeof msgData.model === 'string') {
|
|
103
|
-
modelValue = msgData.model;
|
|
104
|
-
}
|
|
129
|
+
const { modelValue, providerValue } = extractModelInfo(msgData);
|
|
130
|
+
const { inputTokens, outputTokens, cacheRead, cacheWrite } = extractTokenInfo(msgData);
|
|
105
131
|
|
|
106
132
|
result.push({
|
|
107
133
|
role: role === 'user' ? 'user' : role === 'assistant' ? 'assistant' : role,
|
|
108
134
|
content,
|
|
109
135
|
_model: modelValue,
|
|
136
|
+
_provider: providerValue,
|
|
137
|
+
_inputTokens: inputTokens,
|
|
138
|
+
_outputTokens: outputTokens,
|
|
139
|
+
_cacheRead: cacheRead,
|
|
140
|
+
_cacheWrite: cacheWrite,
|
|
110
141
|
});
|
|
111
142
|
}
|
|
112
143
|
|
|
@@ -165,12 +196,19 @@ function getMessagesForSession(sessionId) {
|
|
|
165
196
|
let files;
|
|
166
197
|
try { files = fs.readdirSync(sessionMsgDir).filter(f => f.startsWith('msg_') && f.endsWith('.json')); } catch { return []; }
|
|
167
198
|
|
|
168
|
-
const
|
|
199
|
+
const rawMsgs = [];
|
|
169
200
|
for (const file of files) {
|
|
170
201
|
const msgPath = path.join(sessionMsgDir, file);
|
|
171
202
|
const msg = readJson(msgPath);
|
|
172
203
|
if (!msg || !msg.id) continue;
|
|
204
|
+
rawMsgs.push(msg);
|
|
205
|
+
}
|
|
173
206
|
|
|
207
|
+
// Sort by creation time before building output
|
|
208
|
+
rawMsgs.sort((a, b) => (a.time?.created || 0) - (b.time?.created || 0));
|
|
209
|
+
|
|
210
|
+
const messages = [];
|
|
211
|
+
for (const msg of rawMsgs) {
|
|
174
212
|
// Get parts for this message
|
|
175
213
|
const msgPartDir = path.join(PART_DIR, msg.id);
|
|
176
214
|
const parts = [];
|
|
@@ -213,35 +251,24 @@ function getMessagesForSession(sessionId) {
|
|
|
213
251
|
|
|
214
252
|
const content = contentParts.join('\n');
|
|
215
253
|
if (content) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (typeof msg.modelID === 'string') {
|
|
219
|
-
modelValue = msg.modelID;
|
|
220
|
-
} else if (msg.model && typeof msg.model === 'object' && msg.model.modelID) {
|
|
221
|
-
modelValue = msg.model.modelID;
|
|
222
|
-
} else if (typeof msg.model === 'string') {
|
|
223
|
-
modelValue = msg.model;
|
|
224
|
-
}
|
|
254
|
+
const { modelValue, providerValue } = extractModelInfo(msg);
|
|
255
|
+
const { inputTokens, outputTokens, cacheRead, cacheWrite } = extractTokenInfo(msg);
|
|
225
256
|
|
|
226
257
|
messages.push({
|
|
227
258
|
role: msg.role || 'assistant',
|
|
228
259
|
content,
|
|
229
260
|
_model: modelValue,
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
261
|
+
_provider: providerValue,
|
|
262
|
+
_inputTokens: inputTokens,
|
|
263
|
+
_outputTokens: outputTokens,
|
|
264
|
+
_cacheRead: cacheRead,
|
|
265
|
+
_cacheWrite: cacheWrite,
|
|
234
266
|
_finish: msg.finish,
|
|
235
267
|
});
|
|
236
268
|
}
|
|
237
269
|
}
|
|
238
270
|
|
|
239
|
-
|
|
240
|
-
return messages.sort((a, b) => {
|
|
241
|
-
const aTime = a.time?.created || 0;
|
|
242
|
-
const bTime = b.time?.created || 0;
|
|
243
|
-
return aTime - bTime;
|
|
244
|
-
});
|
|
271
|
+
return messages;
|
|
245
272
|
}
|
|
246
273
|
|
|
247
274
|
// ============================================================
|
package/index.js
CHANGED
|
@@ -6,11 +6,44 @@ const path = require('path');
|
|
|
6
6
|
const os = require('os');
|
|
7
7
|
const { execSync } = require('child_process');
|
|
8
8
|
|
|
9
|
+
// ── Node.js version check ─────────────────────────────────
|
|
10
|
+
const nodeVersion = process.versions.node;
|
|
11
|
+
const [nodeMajor, nodeMinor] = nodeVersion.split('.').map(Number);
|
|
12
|
+
const isNodeSupported =
|
|
13
|
+
(nodeMajor === 20 && nodeMinor >= 19) ||
|
|
14
|
+
(nodeMajor === 22 && nodeMinor >= 12) ||
|
|
15
|
+
nodeMajor === 23 || nodeMajor === 24 || nodeMajor >= 25;
|
|
16
|
+
|
|
17
|
+
if (!isNodeSupported) {
|
|
18
|
+
console.error('');
|
|
19
|
+
console.error(` \x1b[31m✗ Unsupported Node.js version: v${nodeVersion}\x1b[0m`);
|
|
20
|
+
console.error('');
|
|
21
|
+
console.error(` \x1b[1mAgentlytics requires Node.js 20.19+ or 22.12+\x1b[0m`);
|
|
22
|
+
console.error(` \x1b[2mYour current version: v${nodeVersion}\x1b[0m`);
|
|
23
|
+
console.error('');
|
|
24
|
+
console.error(` \x1b[2mTo upgrade, visit: https://nodejs.org\x1b[0m`);
|
|
25
|
+
console.error(` \x1b[2mOr use a version manager: nvm install 22\x1b[0m`);
|
|
26
|
+
console.error('');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
9
30
|
const HOME = os.homedir();
|
|
31
|
+
|
|
32
|
+
// ── Detect package manager ─────────────────────────────────
|
|
33
|
+
function detectPackageManager() {
|
|
34
|
+
const ua = process.env.npm_config_user_agent || '';
|
|
35
|
+
if (ua.startsWith('pnpm/')) return 'pnpm';
|
|
36
|
+
if (ua.startsWith('yarn/')) return 'yarn';
|
|
37
|
+
if (ua.startsWith('bun/')) return 'bun';
|
|
38
|
+
return 'npm';
|
|
39
|
+
}
|
|
40
|
+
const PM = detectPackageManager();
|
|
41
|
+
const PM_RUN = PM === 'npm' ? 'npx' : PM === 'pnpm' ? 'pnpm dlx' : PM === 'yarn' ? 'yarn dlx' : 'bunx';
|
|
10
42
|
const PORT = process.env.PORT || 4637;
|
|
11
43
|
const RELAY_PORT = process.env.RELAY_PORT || 4638;
|
|
12
44
|
const noCache = process.argv.includes('--no-cache');
|
|
13
45
|
const collectOnly = process.argv.includes('--collect');
|
|
46
|
+
const isUiDev = process.argv.includes('--ui-dev');
|
|
14
47
|
const isRelay = process.argv.includes('--relay');
|
|
15
48
|
const joinIndex = process.argv.indexOf('--join');
|
|
16
49
|
const isJoin = joinIndex !== -1;
|
|
@@ -47,7 +80,7 @@ if (isRelay) {
|
|
|
47
80
|
console.log('');
|
|
48
81
|
console.log(chalk.bold(' Share this command with your team:'));
|
|
49
82
|
console.log('');
|
|
50
|
-
console.log(chalk.cyan(`
|
|
83
|
+
console.log(chalk.cyan(` ${PM_RUN} agentlytics --join ${localIp}:${RELAY_PORT} --username <name>`));
|
|
51
84
|
console.log('');
|
|
52
85
|
console.log(chalk.bold(' MCP server endpoint (add to your AI client):'));
|
|
53
86
|
console.log('');
|
|
@@ -76,7 +109,7 @@ if (isJoin) {
|
|
|
76
109
|
let username = usernameIndex !== -1 ? process.argv[usernameIndex + 1] : null;
|
|
77
110
|
|
|
78
111
|
if (!relayAddress) {
|
|
79
|
-
console.error(chalk.red(
|
|
112
|
+
console.error(chalk.red(`\n ✗ Missing relay address. Usage: ${PM_RUN} agentlytics --join <host:port> --username <name>\n`));
|
|
80
113
|
process.exit(1);
|
|
81
114
|
}
|
|
82
115
|
|
|
@@ -135,15 +168,20 @@ console.log('');
|
|
|
135
168
|
const publicIndex = path.join(__dirname, 'public', 'index.html');
|
|
136
169
|
const uiDir = path.join(__dirname, 'ui');
|
|
137
170
|
|
|
138
|
-
if (!collectOnly && !fs.existsSync(publicIndex) && fs.existsSync(uiDir)) {
|
|
171
|
+
if (!collectOnly && !isUiDev && !fs.existsSync(publicIndex) && fs.existsSync(uiDir)) {
|
|
139
172
|
console.log(chalk.cyan(' ⟳ Building dashboard UI (first run)...'));
|
|
140
173
|
try {
|
|
141
174
|
const uiModules = path.join(uiDir, 'node_modules');
|
|
142
175
|
if (fs.existsSync(uiModules)) fs.rmSync(uiModules, { recursive: true, force: true });
|
|
143
176
|
console.log(chalk.dim(' Installing UI dependencies...'));
|
|
144
|
-
|
|
177
|
+
const installCmd = PM === 'npm' ? 'npm install --no-audit --no-fund'
|
|
178
|
+
: PM === 'pnpm' ? 'pnpm install --no-frozen-lockfile'
|
|
179
|
+
: PM === 'yarn' ? 'yarn install'
|
|
180
|
+
: 'bun install';
|
|
181
|
+
const buildCmd = `${PM} run build`;
|
|
182
|
+
execSync(installCmd, { cwd: uiDir, stdio: 'pipe' });
|
|
145
183
|
console.log(chalk.dim(' Compiling frontend...'));
|
|
146
|
-
execSync(
|
|
184
|
+
execSync(buildCmd, { cwd: uiDir, stdio: 'pipe' });
|
|
147
185
|
console.log(chalk.green(' ✓ UI built successfully'));
|
|
148
186
|
} catch (err) {
|
|
149
187
|
console.error(chalk.red(' ✗ UI build failed:'), err.message);
|
|
@@ -152,9 +190,9 @@ if (!collectOnly && !fs.existsSync(publicIndex) && fs.existsSync(uiDir)) {
|
|
|
152
190
|
console.log('');
|
|
153
191
|
}
|
|
154
192
|
|
|
155
|
-
if (!collectOnly && !fs.existsSync(publicIndex)) {
|
|
193
|
+
if (!collectOnly && !isUiDev && !fs.existsSync(publicIndex)) {
|
|
156
194
|
console.error(chalk.red(' ✗ No built UI found at public/index.html'));
|
|
157
|
-
console.error(chalk.dim(
|
|
195
|
+
console.error(chalk.dim(` Run: cd ui && ${PM} install && ${PM} run build`));
|
|
158
196
|
process.exit(1);
|
|
159
197
|
}
|
|
160
198
|
|
|
@@ -365,12 +403,12 @@ const BOT_STYLES = [
|
|
|
365
403
|
}
|
|
366
404
|
|
|
367
405
|
app.listen(port, '0.0.0.0', () => {
|
|
406
|
+
if (isUiDev) return;
|
|
368
407
|
const url = `http://localhost:${port}`;
|
|
369
408
|
console.log(chalk.green(` ✓ Dashboard ready at ${chalk.bold.white(url)}`));
|
|
370
409
|
console.log('');
|
|
371
410
|
console.log(chalk.dim(' Press Ctrl+C to stop\n'));
|
|
372
411
|
|
|
373
|
-
// Auto-open browser
|
|
374
412
|
const open = require('open');
|
|
375
413
|
open(url).catch(() => {});
|
|
376
414
|
});
|