@swarmify/agents-cli 1.10.4 → 1.11.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/CHANGELOG.md +18 -0
- package/README.md +45 -2
- package/dist/commands/__tests__/sessions.test.js +266 -0
- package/dist/commands/__tests__/sessions.test.js.map +1 -1
- package/dist/commands/commands.d.ts.map +1 -1
- package/dist/commands/commands.js +5 -11
- package/dist/commands/commands.js.map +1 -1
- package/dist/commands/exec.d.ts.map +1 -1
- package/dist/commands/exec.js +11 -1
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/hooks.d.ts.map +1 -1
- package/dist/commands/hooks.js +5 -11
- package/dist/commands/hooks.js.map +1 -1
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +109 -43
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/packages.d.ts.map +1 -1
- package/dist/commands/packages.js +92 -42
- package/dist/commands/packages.js.map +1 -1
- package/dist/commands/permissions.d.ts.map +1 -1
- package/dist/commands/permissions.js +38 -34
- package/dist/commands/permissions.js.map +1 -1
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +14 -15
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +2 -0
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/rules.d.ts.map +1 -1
- package/dist/commands/rules.js +5 -11
- package/dist/commands/rules.js.map +1 -1
- package/dist/commands/sessions.d.ts.map +1 -1
- package/dist/commands/sessions.js +157 -30
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +5 -11
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/versions.d.ts.map +1 -1
- package/dist/commands/versions.js +5 -1
- package/dist/commands/versions.js.map +1 -1
- package/dist/commands/view.d.ts.map +1 -1
- package/dist/commands/view.js +36 -29
- package/dist/commands/view.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/__tests__/exec.test.js +34 -1
- package/dist/lib/__tests__/exec.test.js.map +1 -1
- package/dist/lib/agents.d.ts +19 -0
- package/dist/lib/agents.d.ts.map +1 -1
- package/dist/lib/agents.js +177 -16
- package/dist/lib/agents.js.map +1 -1
- package/dist/lib/exec.d.ts +3 -0
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +26 -0
- package/dist/lib/exec.js.map +1 -1
- package/dist/lib/registry.d.ts.map +1 -1
- package/dist/lib/registry.js +8 -0
- package/dist/lib/registry.js.map +1 -1
- package/dist/lib/resources.d.ts +2 -0
- package/dist/lib/resources.d.ts.map +1 -1
- package/dist/lib/resources.js +7 -7
- package/dist/lib/resources.js.map +1 -1
- package/dist/lib/session/discover.d.ts.map +1 -1
- package/dist/lib/session/discover.js +341 -114
- package/dist/lib/session/discover.js.map +1 -1
- package/dist/lib/session/prompt.d.ts +3 -0
- package/dist/lib/session/prompt.d.ts.map +1 -0
- package/dist/lib/session/prompt.js +40 -0
- package/dist/lib/session/prompt.js.map +1 -0
- package/dist/lib/session/render.d.ts.map +1 -1
- package/dist/lib/session/render.js +6 -37
- package/dist/lib/session/render.js.map +1 -1
- package/dist/lib/session/types.d.ts +1 -0
- package/dist/lib/session/types.d.ts.map +1 -1
- package/dist/lib/types.d.ts +1 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/usage.d.ts +1 -0
- package/dist/lib/usage.d.ts.map +1 -1
- package/dist/lib/usage.js +45 -33
- package/dist/lib/usage.js.map +1 -1
- package/dist/lib/versions.d.ts +28 -0
- package/dist/lib/versions.d.ts.map +1 -1
- package/dist/lib/versions.js +181 -1
- package/dist/lib/versions.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import * as crypto from 'crypto';
|
|
|
5
5
|
import * as readline from 'readline';
|
|
6
6
|
import { execSync } from 'child_process';
|
|
7
7
|
import { SESSION_AGENTS } from './types.js';
|
|
8
|
+
import { extractSessionTopic } from './prompt.js';
|
|
8
9
|
const HOME = os.homedir();
|
|
9
10
|
const AGENTS_DIR = path.join(HOME, '.agents');
|
|
10
11
|
const SESSIONS_DIR = path.join(AGENTS_DIR, 'sessions');
|
|
@@ -43,12 +44,15 @@ export async function discoverSessions(options) {
|
|
|
43
44
|
toSave.set(s.id, s);
|
|
44
45
|
}
|
|
45
46
|
saveIndex([...toSave.values()]);
|
|
47
|
+
const projectQuery = options?.project?.trim();
|
|
46
48
|
// Filter by project (case-insensitive substring match)
|
|
47
|
-
if (
|
|
48
|
-
const query =
|
|
49
|
+
if (projectQuery) {
|
|
50
|
+
const query = projectQuery.toLowerCase();
|
|
49
51
|
sessions = sessions.filter(s => s.project?.toLowerCase().includes(query));
|
|
50
52
|
}
|
|
51
|
-
|
|
53
|
+
// An explicit project search should scan across directories instead of
|
|
54
|
+
// intersecting with the default cwd-only scope.
|
|
55
|
+
if (!options?.all && !projectQuery) {
|
|
52
56
|
const currentDir = normalizeCwd(options?.cwd || process.cwd());
|
|
53
57
|
sessions = sessions.filter(s => normalizeCwd(s.cwd) === currentDir);
|
|
54
58
|
}
|
|
@@ -113,18 +117,7 @@ function saveIndex(sessions) {
|
|
|
113
117
|
if (seen.has(s.id))
|
|
114
118
|
continue;
|
|
115
119
|
seen.add(s.id);
|
|
116
|
-
lines.push(JSON.stringify(
|
|
117
|
-
id: s.id,
|
|
118
|
-
shortId: s.shortId,
|
|
119
|
-
agent: s.agent,
|
|
120
|
-
timestamp: s.timestamp,
|
|
121
|
-
project: s.project,
|
|
122
|
-
cwd: s.cwd,
|
|
123
|
-
filePath: s.filePath,
|
|
124
|
-
gitBranch: s.gitBranch,
|
|
125
|
-
version: s.version,
|
|
126
|
-
account: s.account,
|
|
127
|
-
}));
|
|
120
|
+
lines.push(JSON.stringify(s));
|
|
128
121
|
}
|
|
129
122
|
fs.writeFileSync(INDEX_PATH, lines.join('\n') + '\n', 'utf-8');
|
|
130
123
|
}
|
|
@@ -269,41 +262,24 @@ async function discoverClaudeSessions() {
|
|
|
269
262
|
return sessions;
|
|
270
263
|
}
|
|
271
264
|
async function readClaudeMeta(filePath, sessionId, account) {
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
// Look for first user or assistant line with timestamp/cwd
|
|
291
|
-
if ((parsed.type === 'user' || parsed.type === 'assistant') && parsed.timestamp) {
|
|
292
|
-
const cwd = parsed.cwd || '';
|
|
293
|
-
return {
|
|
294
|
-
id: sessionId,
|
|
295
|
-
shortId: sessionId.slice(0, 8),
|
|
296
|
-
agent: 'claude',
|
|
297
|
-
timestamp: parsed.timestamp,
|
|
298
|
-
project: cwd ? path.basename(cwd) : undefined,
|
|
299
|
-
cwd,
|
|
300
|
-
filePath,
|
|
301
|
-
gitBranch: parsed.gitBranch || undefined,
|
|
302
|
-
version: parsed.version || undefined,
|
|
303
|
-
account,
|
|
304
|
-
topic,
|
|
305
|
-
};
|
|
306
|
-
}
|
|
265
|
+
const scan = await scanClaudeSession(filePath);
|
|
266
|
+
if (scan.timestamp) {
|
|
267
|
+
const cwd = scan.cwd || '';
|
|
268
|
+
return {
|
|
269
|
+
id: sessionId,
|
|
270
|
+
shortId: sessionId.slice(0, 8),
|
|
271
|
+
agent: 'claude',
|
|
272
|
+
timestamp: scan.timestamp,
|
|
273
|
+
project: cwd ? path.basename(cwd) : undefined,
|
|
274
|
+
cwd,
|
|
275
|
+
filePath,
|
|
276
|
+
gitBranch: scan.gitBranch,
|
|
277
|
+
version: scan.version,
|
|
278
|
+
account,
|
|
279
|
+
topic: scan.topic,
|
|
280
|
+
messageCount: scan.messageCount,
|
|
281
|
+
tokenCount: scan.tokenCount,
|
|
282
|
+
};
|
|
307
283
|
}
|
|
308
284
|
// Fallback: use file mtime
|
|
309
285
|
const stat = safeStatSync(filePath);
|
|
@@ -314,6 +290,9 @@ async function readClaudeMeta(filePath, sessionId, account) {
|
|
|
314
290
|
timestamp: stat ? stat.mtime.toISOString() : new Date().toISOString(),
|
|
315
291
|
filePath,
|
|
316
292
|
account,
|
|
293
|
+
messageCount: scan.messageCount,
|
|
294
|
+
tokenCount: scan.tokenCount,
|
|
295
|
+
topic: scan.topic,
|
|
317
296
|
};
|
|
318
297
|
}
|
|
319
298
|
// ---------------------------------------------------------------------------
|
|
@@ -388,50 +367,24 @@ async function discoverCodexSessions() {
|
|
|
388
367
|
return sessions;
|
|
389
368
|
}
|
|
390
369
|
async function readCodexMeta(filePath, account) {
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
return null;
|
|
394
|
-
let parsed;
|
|
395
|
-
try {
|
|
396
|
-
parsed = JSON.parse(lines[0]);
|
|
397
|
-
}
|
|
398
|
-
catch {
|
|
399
|
-
return null;
|
|
400
|
-
}
|
|
401
|
-
if (parsed.type !== 'session_meta')
|
|
402
|
-
return null;
|
|
403
|
-
const payload = parsed.payload || {};
|
|
404
|
-
const sessionId = payload.id || '';
|
|
370
|
+
const scan = await scanCodexSession(filePath);
|
|
371
|
+
const sessionId = scan.sessionId || '';
|
|
405
372
|
if (!sessionId)
|
|
406
373
|
return null;
|
|
407
|
-
|
|
408
|
-
let topic;
|
|
409
|
-
for (let i = 1; i < lines.length; i++) {
|
|
410
|
-
try {
|
|
411
|
-
const ev = JSON.parse(lines[i]);
|
|
412
|
-
if (ev.type === 'message' && ev.role === 'user' && ev.content) {
|
|
413
|
-
const text = typeof ev.content === 'string' ? ev.content
|
|
414
|
-
: Array.isArray(ev.content) ? ev.content.find((b) => b.type === 'input_text')?.text : undefined;
|
|
415
|
-
if (text) {
|
|
416
|
-
topic = extractTopic(text);
|
|
417
|
-
break;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
catch { /* malformed event line */ }
|
|
422
|
-
}
|
|
423
|
-
const cwd = payload.cwd || '';
|
|
374
|
+
const cwd = scan.cwd || '';
|
|
424
375
|
return {
|
|
425
376
|
id: sessionId,
|
|
426
377
|
shortId: sessionId.slice(0, 8),
|
|
427
378
|
agent: 'codex',
|
|
428
|
-
timestamp:
|
|
379
|
+
timestamp: scan.timestamp || new Date().toISOString(),
|
|
429
380
|
project: cwd ? path.basename(cwd) : undefined,
|
|
430
381
|
cwd,
|
|
431
382
|
filePath,
|
|
432
|
-
gitBranch:
|
|
433
|
-
version:
|
|
434
|
-
topic,
|
|
383
|
+
gitBranch: scan.gitBranch,
|
|
384
|
+
version: scan.version,
|
|
385
|
+
topic: scan.topic,
|
|
386
|
+
messageCount: scan.messageCount,
|
|
387
|
+
tokenCount: scan.tokenCount,
|
|
435
388
|
account,
|
|
436
389
|
};
|
|
437
390
|
}
|
|
@@ -483,16 +436,16 @@ async function discoverGeminiSessions() {
|
|
|
483
436
|
return sessions;
|
|
484
437
|
}
|
|
485
438
|
function readGeminiMeta(filePath, hashDir, projectMap) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const sessionId =
|
|
494
|
-
const startTime =
|
|
495
|
-
const projectHash =
|
|
439
|
+
let session;
|
|
440
|
+
try {
|
|
441
|
+
session = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
const sessionId = typeof session.sessionId === 'string' ? session.sessionId : '';
|
|
447
|
+
const startTime = typeof session.startTime === 'string' ? session.startTime : '';
|
|
448
|
+
const projectHash = typeof session.projectHash === 'string' ? session.projectHash : '';
|
|
496
449
|
if (!sessionId)
|
|
497
450
|
return null;
|
|
498
451
|
// Resolve project name from hash
|
|
@@ -500,11 +453,31 @@ function readGeminiMeta(filePath, hashDir, projectMap) {
|
|
|
500
453
|
const project = projectInfo?.name || hashDir.slice(0, 12);
|
|
501
454
|
const cwd = projectInfo?.path;
|
|
502
455
|
const stat = safeStatSync(filePath);
|
|
503
|
-
|
|
456
|
+
const messages = Array.isArray(session.messages) ? session.messages : [];
|
|
504
457
|
let topic;
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
458
|
+
let messageCount = 0;
|
|
459
|
+
let tokenCount = 0;
|
|
460
|
+
let sawTokenCount = false;
|
|
461
|
+
for (const message of messages) {
|
|
462
|
+
if (message.type === 'user') {
|
|
463
|
+
const text = extractGeminiMessageText(message.content);
|
|
464
|
+
if (text) {
|
|
465
|
+
messageCount++;
|
|
466
|
+
if (!topic)
|
|
467
|
+
topic = extractSessionTopic(text);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else if (message.type === 'gemini') {
|
|
471
|
+
if (extractGeminiMessageText(message.content)) {
|
|
472
|
+
messageCount++;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
const total = getGeminiTokenCount(message.tokens);
|
|
476
|
+
if (total !== null) {
|
|
477
|
+
tokenCount += total;
|
|
478
|
+
sawTokenCount = true;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
508
481
|
return {
|
|
509
482
|
id: sessionId,
|
|
510
483
|
shortId: sessionId.slice(0, 8),
|
|
@@ -514,6 +487,8 @@ function readGeminiMeta(filePath, hashDir, projectMap) {
|
|
|
514
487
|
cwd,
|
|
515
488
|
filePath,
|
|
516
489
|
topic,
|
|
490
|
+
messageCount,
|
|
491
|
+
tokenCount: sawTokenCount ? tokenCount : undefined,
|
|
517
492
|
};
|
|
518
493
|
}
|
|
519
494
|
function buildGeminiProjectMap() {
|
|
@@ -598,9 +573,32 @@ async function discoverOpenCodeSessions() {
|
|
|
598
573
|
// Query sessions. time_created is millisecond epoch. Limit to 200 most recent.
|
|
599
574
|
// Use session.title as topic (OpenCode auto-generates good titles).
|
|
600
575
|
const query = `
|
|
601
|
-
SELECT
|
|
602
|
-
|
|
603
|
-
|
|
576
|
+
SELECT
|
|
577
|
+
s.id,
|
|
578
|
+
s.title,
|
|
579
|
+
s.directory,
|
|
580
|
+
s.version,
|
|
581
|
+
s.time_created,
|
|
582
|
+
COALESCE(stats.message_count, 0),
|
|
583
|
+
stats.token_count,
|
|
584
|
+
COALESCE(stats.has_token_data, 0)
|
|
585
|
+
FROM session s
|
|
586
|
+
LEFT JOIN (
|
|
587
|
+
SELECT
|
|
588
|
+
session_id,
|
|
589
|
+
COUNT(*) AS message_count,
|
|
590
|
+
SUM(
|
|
591
|
+
COALESCE(json_extract(data, '$.tokens.input'), 0) +
|
|
592
|
+
COALESCE(json_extract(data, '$.tokens.output'), 0) +
|
|
593
|
+
COALESCE(json_extract(data, '$.tokens.reasoning'), 0) +
|
|
594
|
+
COALESCE(json_extract(data, '$.tokens.cache.read'), 0) +
|
|
595
|
+
COALESCE(json_extract(data, '$.tokens.cache.write'), 0)
|
|
596
|
+
) AS token_count,
|
|
597
|
+
MAX(CASE WHEN json_type(data, '$.tokens') IS NOT NULL THEN 1 ELSE 0 END) AS has_token_data
|
|
598
|
+
FROM message
|
|
599
|
+
GROUP BY session_id
|
|
600
|
+
) stats ON stats.session_id = s.id
|
|
601
|
+
WHERE s.parent_id IS NULL
|
|
604
602
|
ORDER BY time_created DESC
|
|
605
603
|
LIMIT 200;
|
|
606
604
|
`.replace(/\n/g, ' ');
|
|
@@ -609,10 +607,13 @@ async function discoverOpenCodeSessions() {
|
|
|
609
607
|
for (const line of out.split('\n')) {
|
|
610
608
|
if (!line.trim())
|
|
611
609
|
continue;
|
|
612
|
-
const [id, title, directory, version, timeCreatedStr] = line.split('|||');
|
|
610
|
+
const [id, title, directory, version, timeCreatedStr, messageCountStr, tokenCountStr, hasTokenDataStr] = line.split('|||');
|
|
613
611
|
if (!id)
|
|
614
612
|
continue;
|
|
615
613
|
const timeCreated = parseInt(timeCreatedStr, 10);
|
|
614
|
+
const messageCount = parseInt(messageCountStr, 10);
|
|
615
|
+
const tokenCount = parseInt(tokenCountStr, 10);
|
|
616
|
+
const hasTokenData = hasTokenDataStr === '1';
|
|
616
617
|
const timestamp = isNaN(timeCreated) ? new Date().toISOString() : new Date(timeCreated).toISOString();
|
|
617
618
|
const topic = title || undefined;
|
|
618
619
|
sessions.push({
|
|
@@ -626,6 +627,8 @@ async function discoverOpenCodeSessions() {
|
|
|
626
627
|
version: version || undefined,
|
|
627
628
|
account,
|
|
628
629
|
topic,
|
|
630
|
+
messageCount: Number.isNaN(messageCount) ? undefined : messageCount,
|
|
631
|
+
tokenCount: hasTokenData && !Number.isNaN(tokenCount) ? tokenCount : undefined,
|
|
629
632
|
});
|
|
630
633
|
}
|
|
631
634
|
return sessions;
|
|
@@ -722,6 +725,142 @@ async function discoverOpenClawSessions() {
|
|
|
722
725
|
}
|
|
723
726
|
return sessions;
|
|
724
727
|
}
|
|
728
|
+
async function scanClaudeSession(filePath) {
|
|
729
|
+
const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
|
|
730
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
731
|
+
let timestamp;
|
|
732
|
+
let cwd;
|
|
733
|
+
let gitBranch;
|
|
734
|
+
let version;
|
|
735
|
+
let topic;
|
|
736
|
+
let messageCount = 0;
|
|
737
|
+
let tokenCount = 0;
|
|
738
|
+
let sawTokenCount = false;
|
|
739
|
+
const seenAssistantIds = new Set();
|
|
740
|
+
try {
|
|
741
|
+
for await (const line of rl) {
|
|
742
|
+
if (!line.trim())
|
|
743
|
+
continue;
|
|
744
|
+
let parsed;
|
|
745
|
+
try {
|
|
746
|
+
parsed = JSON.parse(line);
|
|
747
|
+
}
|
|
748
|
+
catch {
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
if (!timestamp && (parsed.type === 'user' || parsed.type === 'assistant') && parsed.timestamp) {
|
|
752
|
+
timestamp = parsed.timestamp;
|
|
753
|
+
cwd = parsed.cwd || '';
|
|
754
|
+
gitBranch = parsed.gitBranch || undefined;
|
|
755
|
+
version = parsed.version || undefined;
|
|
756
|
+
}
|
|
757
|
+
if (parsed.type === 'user') {
|
|
758
|
+
const text = extractClaudeUserText(parsed);
|
|
759
|
+
if (text) {
|
|
760
|
+
messageCount++;
|
|
761
|
+
if (!topic)
|
|
762
|
+
topic = extractSessionTopic(text);
|
|
763
|
+
}
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
if (parsed.type !== 'assistant')
|
|
767
|
+
continue;
|
|
768
|
+
const assistantId = typeof parsed.message?.id === 'string'
|
|
769
|
+
? parsed.message.id
|
|
770
|
+
: typeof parsed.uuid === 'string'
|
|
771
|
+
? parsed.uuid
|
|
772
|
+
: undefined;
|
|
773
|
+
const logicalId = assistantId || `${parsed.timestamp || ''}:${seenAssistantIds.size}`;
|
|
774
|
+
if (seenAssistantIds.has(logicalId))
|
|
775
|
+
continue;
|
|
776
|
+
seenAssistantIds.add(logicalId);
|
|
777
|
+
messageCount++;
|
|
778
|
+
const usage = getClaudeUsageTotal(parsed.message?.usage || parsed.usage);
|
|
779
|
+
if (usage !== null) {
|
|
780
|
+
tokenCount += usage;
|
|
781
|
+
sawTokenCount = true;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
finally {
|
|
786
|
+
rl.close();
|
|
787
|
+
stream.destroy();
|
|
788
|
+
}
|
|
789
|
+
return {
|
|
790
|
+
timestamp,
|
|
791
|
+
cwd,
|
|
792
|
+
gitBranch,
|
|
793
|
+
version,
|
|
794
|
+
topic,
|
|
795
|
+
messageCount,
|
|
796
|
+
tokenCount: sawTokenCount ? tokenCount : undefined,
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
async function scanCodexSession(filePath) {
|
|
800
|
+
const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
|
|
801
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
802
|
+
let sessionId;
|
|
803
|
+
let timestamp;
|
|
804
|
+
let cwd;
|
|
805
|
+
let gitBranch;
|
|
806
|
+
let version;
|
|
807
|
+
let topic;
|
|
808
|
+
let messageCount = 0;
|
|
809
|
+
let tokenCount;
|
|
810
|
+
try {
|
|
811
|
+
for await (const line of rl) {
|
|
812
|
+
if (!line.trim())
|
|
813
|
+
continue;
|
|
814
|
+
let parsed;
|
|
815
|
+
try {
|
|
816
|
+
parsed = JSON.parse(line);
|
|
817
|
+
}
|
|
818
|
+
catch {
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
if (parsed.type === 'session_meta') {
|
|
822
|
+
const payload = parsed.payload || {};
|
|
823
|
+
sessionId = payload.id || sessionId;
|
|
824
|
+
timestamp = payload.timestamp || parsed.timestamp || timestamp;
|
|
825
|
+
cwd = payload.cwd || cwd;
|
|
826
|
+
gitBranch = payload.git?.branch || gitBranch;
|
|
827
|
+
version = payload.version || version;
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
if (parsed.type === 'response_item' && parsed.payload?.type === 'message') {
|
|
831
|
+
const role = parsed.payload.role === 'user' || parsed.payload.role === 'developer'
|
|
832
|
+
? 'user'
|
|
833
|
+
: 'assistant';
|
|
834
|
+
const text = extractCodexMessageText(parsed.payload.content, role);
|
|
835
|
+
if (!text)
|
|
836
|
+
continue;
|
|
837
|
+
messageCount++;
|
|
838
|
+
if (role === 'user' && !topic)
|
|
839
|
+
topic = extractSessionTopic(text);
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
if (parsed.type === 'event_msg' && parsed.payload?.type === 'token_count') {
|
|
843
|
+
const total = getCodexTokenCount(parsed.payload.info?.total_token_usage);
|
|
844
|
+
if (total !== null)
|
|
845
|
+
tokenCount = total;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
finally {
|
|
850
|
+
rl.close();
|
|
851
|
+
stream.destroy();
|
|
852
|
+
}
|
|
853
|
+
return {
|
|
854
|
+
sessionId,
|
|
855
|
+
timestamp,
|
|
856
|
+
cwd,
|
|
857
|
+
gitBranch,
|
|
858
|
+
version,
|
|
859
|
+
topic,
|
|
860
|
+
messageCount,
|
|
861
|
+
tokenCount,
|
|
862
|
+
};
|
|
863
|
+
}
|
|
725
864
|
// ---------------------------------------------------------------------------
|
|
726
865
|
// Utilities
|
|
727
866
|
// ---------------------------------------------------------------------------
|
|
@@ -777,11 +916,6 @@ export function walkForFiles(dir, ext, limit) {
|
|
|
777
916
|
results.sort((a, b) => b.mtime - a.mtime);
|
|
778
917
|
return results.slice(0, limit).map(r => r.path);
|
|
779
918
|
}
|
|
780
|
-
function extractJsonField(text, field) {
|
|
781
|
-
const re = new RegExp(`"${field}"\\s*:\\s*"([^"]*)"`, 'i');
|
|
782
|
-
const match = text.match(re);
|
|
783
|
-
return match ? match[1] : '';
|
|
784
|
-
}
|
|
785
919
|
function sha256(input) {
|
|
786
920
|
return crypto.createHash('sha256').update(input).digest('hex');
|
|
787
921
|
}
|
|
@@ -801,11 +935,104 @@ function safeRealpathSync(p) {
|
|
|
801
935
|
return null;
|
|
802
936
|
}
|
|
803
937
|
}
|
|
804
|
-
function
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
938
|
+
function extractClaudeUserText(parsed) {
|
|
939
|
+
if (parsed.isMeta === true)
|
|
940
|
+
return undefined;
|
|
941
|
+
const content = parsed.message?.content;
|
|
942
|
+
if (typeof content === 'string') {
|
|
943
|
+
const text = content.trim();
|
|
944
|
+
return isLocalCommandMessage(text) ? undefined : text || undefined;
|
|
945
|
+
}
|
|
946
|
+
if (!Array.isArray(content))
|
|
947
|
+
return undefined;
|
|
948
|
+
const text = content
|
|
949
|
+
.filter((block) => block.type === 'text')
|
|
950
|
+
.map((block) => String(block.text || '').trim())
|
|
951
|
+
.find((value) => value && !value.startsWith('[Request interrupted'));
|
|
952
|
+
if (!text || isLocalCommandMessage(text))
|
|
953
|
+
return undefined;
|
|
954
|
+
return text;
|
|
955
|
+
}
|
|
956
|
+
function isLocalCommandMessage(text) {
|
|
957
|
+
return /<local-command-caveat>|<bash-(input|stdout|stderr)>/i.test(text);
|
|
958
|
+
}
|
|
959
|
+
function getClaudeUsageTotal(usage) {
|
|
960
|
+
if (!usage || typeof usage !== 'object')
|
|
961
|
+
return null;
|
|
962
|
+
return sumKnownNumbers([
|
|
963
|
+
usage.input_tokens,
|
|
964
|
+
usage.output_tokens,
|
|
965
|
+
usage.cache_creation_input_tokens,
|
|
966
|
+
usage.cache_read_input_tokens,
|
|
967
|
+
]);
|
|
968
|
+
}
|
|
969
|
+
function extractCodexMessageText(contentBlocks, role) {
|
|
970
|
+
if (!Array.isArray(contentBlocks))
|
|
971
|
+
return undefined;
|
|
972
|
+
const matches = role === 'user'
|
|
973
|
+
? contentBlocks.filter((block) => block.type === 'input_text')
|
|
974
|
+
: contentBlocks.filter((block) => block.type === 'output_text');
|
|
975
|
+
const text = matches
|
|
976
|
+
.map((block) => String(block.text || '').trim())
|
|
977
|
+
.find((value) => {
|
|
978
|
+
if (!value)
|
|
979
|
+
return false;
|
|
980
|
+
if (role === 'user' && (value.length >= 2000 || value.includes('<permissions instructions>') || value.startsWith('# AGENTS.md instructions'))) {
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
return true;
|
|
984
|
+
});
|
|
985
|
+
return text || undefined;
|
|
986
|
+
}
|
|
987
|
+
function getCodexTokenCount(totalTokenUsage) {
|
|
988
|
+
if (!totalTokenUsage || typeof totalTokenUsage !== 'object')
|
|
989
|
+
return null;
|
|
990
|
+
return sumKnownNumbers([
|
|
991
|
+
totalTokenUsage.input_tokens,
|
|
992
|
+
totalTokenUsage.cached_input_tokens,
|
|
993
|
+
totalTokenUsage.output_tokens,
|
|
994
|
+
totalTokenUsage.reasoning_output_tokens,
|
|
995
|
+
]);
|
|
996
|
+
}
|
|
997
|
+
function extractGeminiMessageText(content) {
|
|
998
|
+
if (typeof content === 'string')
|
|
999
|
+
return content.trim();
|
|
1000
|
+
if (Array.isArray(content)) {
|
|
1001
|
+
return content
|
|
1002
|
+
.map((part) => {
|
|
1003
|
+
if (typeof part === 'string')
|
|
1004
|
+
return part;
|
|
1005
|
+
if (typeof part?.text === 'string')
|
|
1006
|
+
return part.text;
|
|
1007
|
+
return '';
|
|
1008
|
+
})
|
|
1009
|
+
.join('\n')
|
|
1010
|
+
.trim();
|
|
1011
|
+
}
|
|
1012
|
+
return '';
|
|
1013
|
+
}
|
|
1014
|
+
function getGeminiTokenCount(tokens) {
|
|
1015
|
+
if (!tokens || typeof tokens !== 'object')
|
|
1016
|
+
return null;
|
|
1017
|
+
if (typeof tokens.total === 'number')
|
|
1018
|
+
return tokens.total;
|
|
1019
|
+
return sumKnownNumbers([
|
|
1020
|
+
tokens.input,
|
|
1021
|
+
tokens.output,
|
|
1022
|
+
tokens.cached,
|
|
1023
|
+
tokens.thoughts,
|
|
1024
|
+
tokens.tool,
|
|
1025
|
+
]);
|
|
1026
|
+
}
|
|
1027
|
+
function sumKnownNumbers(values) {
|
|
1028
|
+
let total = 0;
|
|
1029
|
+
let found = false;
|
|
1030
|
+
for (const value of values) {
|
|
1031
|
+
if (typeof value !== 'number' || Number.isNaN(value))
|
|
1032
|
+
continue;
|
|
1033
|
+
total += value;
|
|
1034
|
+
found = true;
|
|
1035
|
+
}
|
|
1036
|
+
return found ? total : null;
|
|
810
1037
|
}
|
|
811
1038
|
//# sourceMappingURL=discover.js.map
|