coding-tool-x 3.8.0 → 3.8.1
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 +4 -6
- package/package.json +2 -2
- package/src/config/paths.js +1 -0
- package/src/server/api/codex-sessions.js +14 -14
- package/src/server/api/gemini-sessions.js +52 -14
- package/src/server/api/omp-projects.js +2 -2
- package/src/server/api/omp-sessions.js +61 -23
- package/src/server/api/sessions.js +44 -2
- package/src/server/services/codex-sessions.js +41 -233
- package/src/server/services/dashboard-snapshot-worker.js +5 -5
- package/src/server/services/gemini-sessions.js +67 -157
- package/src/server/services/omp-sessions.js +102 -105
- package/src/server/services/opencode-sessions.js +339 -546
- package/src/server/services/session-converter.js +4 -4
- package/src/server/services/session-history-adapters/claude.js +235 -0
- package/src/server/services/session-history-adapters/codex.js +158 -0
- package/src/server/services/session-history-adapters/gemini.js +292 -0
- package/src/server/services/session-history-adapters/index.js +20 -0
- package/src/server/services/session-history-adapters/omp.js +256 -0
- package/src/server/services/session-history-index.js +930 -0
- package/src/server/services/session-history-worker.js +98 -0
- package/src/server/services/session-snapshot-worker.js +7 -7
- package/src/server/services/sessions.js +167 -775
- package/src/server/services/sqlite-connection.js +78 -0
- package/src/server/services/workspace-service.js +3 -3
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { PATHS, HOME_DIR } = require('../../config/paths');
|
|
4
|
+
const {
|
|
5
|
+
listProjects: idxListProjects,
|
|
6
|
+
listSessions: idxListSessions,
|
|
7
|
+
getRecentSessions: idxGetRecent,
|
|
8
|
+
searchSessions: idxSearch,
|
|
9
|
+
getSessionStatus: idxGetSessionStatus,
|
|
10
|
+
} = require('./session-history-index');
|
|
4
11
|
const { getOmpCommand, getOmpPaths, resolveOmpRuntime } = require('./omp-config');
|
|
5
12
|
|
|
6
13
|
const PROJECT_ORDER_FILE = PATHS.ompProjectOrder;
|
|
@@ -286,18 +293,16 @@ function scanSessionFiles(rootDir = getOmpSessionPaths().sessions) {
|
|
|
286
293
|
return files;
|
|
287
294
|
}
|
|
288
295
|
|
|
289
|
-
function getAllSessions() {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
.filter(Boolean)
|
|
300
|
-
.sort((a, b) => (b.mtimeMs || 0) - (a.mtimeMs || 0));
|
|
296
|
+
async function getAllSessions(options = {}) {
|
|
297
|
+
const projects = await idxListProjects('omp', options);
|
|
298
|
+
const groups = await Promise.all(projects.map(project => idxListSessions('omp', project.name, options)));
|
|
299
|
+
return groups.flat().map(session => normalizeSession({
|
|
300
|
+
...session,
|
|
301
|
+
mtime: new Date(session.mtime).toISOString(),
|
|
302
|
+
mtimeMs: session.mtime,
|
|
303
|
+
directory: session.projectFullPath,
|
|
304
|
+
usage: session.tokens
|
|
305
|
+
}));
|
|
301
306
|
}
|
|
302
307
|
|
|
303
308
|
function getOmpUsageEvents(rootDir = getOmpSessionPaths().sessions) {
|
|
@@ -350,32 +355,20 @@ function loadSessionOrder() {
|
|
|
350
355
|
return safeReadJson(SESSION_ORDER_FILE, {});
|
|
351
356
|
}
|
|
352
357
|
|
|
353
|
-
function getProjects(_options = {}) {
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
mtimeMs: 0,
|
|
367
|
-
source: 'omp'
|
|
368
|
-
};
|
|
369
|
-
existing.sessionCount += 1;
|
|
370
|
-
if ((session.mtimeMs || 0) > (existing.mtimeMs || 0)) {
|
|
371
|
-
existing.latestSession = session.sessionId;
|
|
372
|
-
existing.mtime = session.mtime;
|
|
373
|
-
existing.mtimeMs = session.mtimeMs;
|
|
374
|
-
}
|
|
375
|
-
projectMap.set(key, existing);
|
|
376
|
-
});
|
|
358
|
+
async function getProjects(_options = {}) {
|
|
359
|
+
const idxProjects = await idxListProjects('omp');
|
|
360
|
+
const projects = idxProjects.map(p => ({
|
|
361
|
+
name: p.name,
|
|
362
|
+
path: p.fullPath || p.path || '',
|
|
363
|
+
fullPath: p.fullPath || '',
|
|
364
|
+
displayName: p.displayName || p.name,
|
|
365
|
+
sessionCount: p.sessionCount || 0,
|
|
366
|
+
latestSession: p.latestSession || null,
|
|
367
|
+
mtime: p.lastUsed || null,
|
|
368
|
+
mtimeMs: p.lastUsed ? new Date(p.lastUsed).getTime() : 0,
|
|
369
|
+
source: 'omp'
|
|
370
|
+
}));
|
|
377
371
|
|
|
378
|
-
const projects = Array.from(projectMap.values());
|
|
379
372
|
const order = loadProjectOrder();
|
|
380
373
|
projects.sort((a, b) => {
|
|
381
374
|
const ai = order.indexOf(a.name);
|
|
@@ -390,10 +383,26 @@ function getProjects(_options = {}) {
|
|
|
390
383
|
return projects;
|
|
391
384
|
}
|
|
392
385
|
|
|
393
|
-
function getSessionsByProject(projectName, _options = {}) {
|
|
386
|
+
async function getSessionsByProject(projectName, _options = {}) {
|
|
387
|
+
const idxSessions = await idxListSessions('omp', projectName);
|
|
388
|
+
const sessions = idxSessions.map(s => ({
|
|
389
|
+
sessionId: s.sessionId,
|
|
390
|
+
filePath: s.filePath,
|
|
391
|
+
firstMessage: s.firstMessage,
|
|
392
|
+
gitBranch: s.gitBranch,
|
|
393
|
+
provider: s.provider,
|
|
394
|
+
model: s.model,
|
|
395
|
+
size: s.size,
|
|
396
|
+
mtime: new Date(s.mtime).toISOString(),
|
|
397
|
+
mtimeMs: s.mtime,
|
|
398
|
+
directory: s.projectFullPath,
|
|
399
|
+
cwd: s.projectFullPath,
|
|
400
|
+
messageCount: s.messageCount,
|
|
401
|
+
usage: s.tokens
|
|
402
|
+
}));
|
|
403
|
+
|
|
394
404
|
const orderMap = loadSessionOrder();
|
|
395
405
|
const order = Array.isArray(orderMap[projectName]) ? orderMap[projectName] : [];
|
|
396
|
-
const sessions = getAllSessions().filter(session => session.projectName === projectName);
|
|
397
406
|
sessions.sort((a, b) => {
|
|
398
407
|
const ai = order.indexOf(a.sessionId);
|
|
399
408
|
const bi = order.indexOf(b.sessionId);
|
|
@@ -427,83 +436,71 @@ function normalizeSession(session) {
|
|
|
427
436
|
};
|
|
428
437
|
}
|
|
429
438
|
|
|
430
|
-
function getSessionById(sessionId) {
|
|
431
|
-
|
|
439
|
+
async function getSessionById(sessionId) {
|
|
440
|
+
const status = await idxGetSessionStatus('omp', sessionId);
|
|
441
|
+
if (!status) return null;
|
|
442
|
+
return {
|
|
443
|
+
sessionId: status.sessionId,
|
|
444
|
+
filePath: status.filePath,
|
|
445
|
+
size: status.size,
|
|
446
|
+
mtime: new Date(status.lastModified).toISOString(),
|
|
447
|
+
mtimeMs: status.lastModified,
|
|
448
|
+
source: 'omp',
|
|
449
|
+
firstMessage: null,
|
|
450
|
+
gitBranch: null,
|
|
451
|
+
forkedFrom: null,
|
|
452
|
+
provider: '',
|
|
453
|
+
model: '',
|
|
454
|
+
directory: null,
|
|
455
|
+
messageCount: 0,
|
|
456
|
+
tokens: { input: 0, output: 0, cached: 0, reasoning: 0, total: 0 }
|
|
457
|
+
};
|
|
432
458
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const session = getSessionById(sessionId);
|
|
459
|
+
async function getSessionMessages(sessionId) {
|
|
460
|
+
const session = await getSessionById(sessionId);
|
|
436
461
|
if (!session) {
|
|
437
462
|
throw new Error('Session not found');
|
|
438
463
|
}
|
|
439
464
|
return readJsonLines(session.filePath).map(convertOmpEntry).filter(Boolean);
|
|
440
465
|
}
|
|
441
466
|
|
|
442
|
-
function getRecentSessions(limit = 5) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
const end = Math.min(text.length, index + needle.length + contextLength);
|
|
461
|
-
matches.push({
|
|
462
|
-
role: message.role || message.type,
|
|
463
|
-
context: text.slice(start, end),
|
|
464
|
-
timestamp: message.timestamp || null
|
|
465
|
-
});
|
|
466
|
-
});
|
|
467
|
-
if (matches.length === 0) return null;
|
|
468
|
-
return {
|
|
469
|
-
...normalizeSession(session),
|
|
470
|
-
projectName: session.projectName,
|
|
471
|
-
projectDisplayName: getDisplayName(session.cwd),
|
|
472
|
-
matchCount: matches.length,
|
|
473
|
-
matches
|
|
474
|
-
};
|
|
475
|
-
})
|
|
476
|
-
.filter(Boolean);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
function deleteSession(sessionId) {
|
|
480
|
-
const session = getSessionById(sessionId);
|
|
481
|
-
if (!session) {
|
|
482
|
-
throw new Error('Session not found');
|
|
483
|
-
}
|
|
467
|
+
async function getRecentSessions(limit = 5) {
|
|
468
|
+
const indexed = await idxGetRecent('omp', limit);
|
|
469
|
+
return indexed.map(session => normalizeSession({
|
|
470
|
+
...session,
|
|
471
|
+
mtime: new Date(session.mtime).toISOString(),
|
|
472
|
+
mtimeMs: session.mtime,
|
|
473
|
+
directory: session.projectFullPath,
|
|
474
|
+
usage: session.tokens
|
|
475
|
+
}));
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async function searchSessions(keyword, contextLength = 35, projectName = null) {
|
|
479
|
+
return idxSearch('omp', keyword, { contextLength, projectName });
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async function deleteSession(sessionId) {
|
|
483
|
+
const session = await getSessionById(sessionId);
|
|
484
|
+
if (!session) throw new Error('Session not found');
|
|
484
485
|
fs.unlinkSync(session.filePath);
|
|
485
486
|
return { success: true };
|
|
486
487
|
}
|
|
487
488
|
|
|
488
|
-
function deleteProject(projectName) {
|
|
489
|
-
const sessions =
|
|
490
|
-
if (sessions.length === 0)
|
|
491
|
-
throw new Error('Project not found');
|
|
492
|
-
}
|
|
489
|
+
async function deleteProject(projectName) {
|
|
490
|
+
const sessions = await getSessionsByProject(projectName);
|
|
491
|
+
if (sessions.length === 0) throw new Error('Project not found');
|
|
493
492
|
sessions.forEach(session => fs.unlinkSync(session.filePath));
|
|
494
493
|
return { success: true, deletedSessions: sessions.length };
|
|
495
494
|
}
|
|
496
495
|
|
|
497
|
-
function forkSession(sessionId) {
|
|
498
|
-
const session = getSessionById(sessionId);
|
|
499
|
-
if (!session)
|
|
500
|
-
|
|
501
|
-
}
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
fs.copyFileSync(session.filePath, targetPath);
|
|
506
|
-
return { success: true, newSessionId: parseOmpSessionId(targetPath), filePath: targetPath };
|
|
496
|
+
async function forkSession(sessionId) {
|
|
497
|
+
const session = await getSessionById(sessionId);
|
|
498
|
+
if (!session) throw new Error('Session not found');
|
|
499
|
+
const newSessionId = crypto.randomUUID();
|
|
500
|
+
const newFile = path.join(path.dirname(session.filePath), `${newSessionId}.jsonl`);
|
|
501
|
+
const content = fs.readFileSync(session.filePath, 'utf8');
|
|
502
|
+
fs.writeFileSync(newFile, content, 'utf8');
|
|
503
|
+
return { success: true, sessionId: newSessionId, filePath: newFile, forkedFrom: sessionId };
|
|
507
504
|
}
|
|
508
505
|
|
|
509
506
|
function saveProjectOrder(order) {
|
|
@@ -516,11 +513,11 @@ function saveSessionOrder(projectName, order) {
|
|
|
516
513
|
safeWriteJson(SESSION_ORDER_FILE, data);
|
|
517
514
|
}
|
|
518
515
|
|
|
519
|
-
function getProjectAndSessionCounts(
|
|
520
|
-
const
|
|
516
|
+
async function getProjectAndSessionCounts(options = {}) {
|
|
517
|
+
const projects = await idxListProjects('omp', options);
|
|
521
518
|
return {
|
|
522
|
-
projectCount:
|
|
523
|
-
sessionCount:
|
|
519
|
+
projectCount: projects.length,
|
|
520
|
+
sessionCount: projects.reduce((sum, project) => sum + (project.sessionCount || 0), 0)
|
|
524
521
|
};
|
|
525
522
|
}
|
|
526
523
|
|