codesesh 0.2.0 → 0.3.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/README.md +6 -4
- package/dist/{chunk-2UNXB2D3.js → chunk-UQI7CTEK.js} +435 -68
- package/dist/chunk-UQI7CTEK.js.map +1 -0
- package/dist/{dist-6EV6SS6N.js → dist-ONDV5GVR.js} +10 -4
- package/dist/index.js +42 -20
- package/dist/index.js.map +1 -1
- package/dist/web/assets/index-jHkWNiQR.css +2 -0
- package/dist/web/assets/index-qhuN0bxX.js +67 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/chunk-2UNXB2D3.js.map +0 -1
- package/dist/web/assets/index-B4PtJ1VM.css +0 -2
- package/dist/web/assets/index-BzkyjHEA.js +0 -67
- /package/dist/{dist-6EV6SS6N.js.map → dist-ONDV5GVR.js.map} +0 -0
package/README.md
CHANGED
|
@@ -19,9 +19,11 @@ Your browser will open at `http://localhost:4321` with all your sessions ready t
|
|
|
19
19
|
## Features
|
|
20
20
|
|
|
21
21
|
- **Unified Timeline** — Browse sessions across all your AI agents in a single, searchable interface
|
|
22
|
+
- **Full-Text Search** — Search across session titles and conversation content with highlighted matches
|
|
22
23
|
- **Dashboard & Activity Trends** — See totals, daily activity, agent distribution, and recent sessions
|
|
23
24
|
- **Full Conversation Replay** — Read every message, tool call, and reasoning step exactly as it happened
|
|
24
25
|
- **Cost & Token Visibility** — See exactly how many tokens and dollars each session consumed
|
|
26
|
+
- **SQLite-Backed Cache & Search Index** — Restore session lists quickly and reuse the same local store for search
|
|
25
27
|
- **Zero Configuration** — Just run it. CodeSesh auto-discovers everything on your filesystem
|
|
26
28
|
- **100% Local & Private** — Nothing leaves your machine. No accounts, no cloud sync, no telemetry
|
|
27
29
|
- **Live Refresh** — Local session changes are picked up automatically while the server is running
|
|
@@ -45,7 +47,7 @@ npx codesesh
|
|
|
45
47
|
# Choose a custom port
|
|
46
48
|
npx codesesh --port 8080
|
|
47
49
|
|
|
48
|
-
# Only show sessions
|
|
50
|
+
# Only show sessions active in the last 3 days
|
|
49
51
|
npx codesesh --days 3
|
|
50
52
|
|
|
51
53
|
# Jump directly to a session
|
|
@@ -69,11 +71,11 @@ npx codesesh --trace
|
|
|
69
71
|
| Flag | Alias | Default | Description |
|
|
70
72
|
| ----------- | ----- | ------- | ----------------------------------------------------------- |
|
|
71
73
|
| `--port` | `-p` | `4321` | HTTP server port |
|
|
72
|
-
| `--days` | `-d` | `7` | Only include sessions
|
|
74
|
+
| `--days` | `-d` | `7` | Only include sessions active in the last N days (`0` = all time) |
|
|
73
75
|
| `--cwd` | — | — | Filter to sessions from a project directory |
|
|
74
76
|
| `--agent` | `-a` | all | Filter to specific agent(s), comma-separated |
|
|
75
|
-
| `--from` | — | — | Sessions
|
|
76
|
-
| `--to` | — | — | Sessions
|
|
77
|
+
| `--from` | — | — | Sessions active after this date `YYYY-MM-DD` |
|
|
78
|
+
| `--to` | — | — | Sessions active before this date `YYYY-MM-DD` |
|
|
77
79
|
| `--session` | `-s` | — | Directly open a session (`agent://session-id`) |
|
|
78
80
|
| `--json` | `-j` | `false` | Output JSON and exit (no server) |
|
|
79
81
|
| `--no-open` | — | `false` | Don't auto-open the browser |
|
|
@@ -10,16 +10,18 @@ import { readFileSync } from "fs";
|
|
|
10
10
|
import { basename } from "path";
|
|
11
11
|
import { existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
12
12
|
import { join as join3 } from "path";
|
|
13
|
+
import { mkdirSync } from "fs";
|
|
14
|
+
import { dirname as dirname2 } from "path";
|
|
13
15
|
import { createRequire } from "module";
|
|
14
16
|
import { createHash } from "crypto";
|
|
15
17
|
import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
|
|
16
|
-
import { join as join4, basename as basename3, dirname as
|
|
18
|
+
import { join as join4, basename as basename3, dirname as dirname3 } from "path";
|
|
17
19
|
import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
|
|
18
20
|
import { join as join5, basename as basename4 } from "path";
|
|
19
21
|
import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync5 } from "fs";
|
|
20
22
|
import { join as join6, normalize } from "path";
|
|
21
23
|
import { resolve, sep } from "path";
|
|
22
|
-
import { existsSync as existsSync7,
|
|
24
|
+
import { existsSync as existsSync7, rmSync, unlinkSync } from "fs";
|
|
23
25
|
import { join as join7 } from "path";
|
|
24
26
|
import { homedir as homedir2 } from "os";
|
|
25
27
|
var registrations = [];
|
|
@@ -982,6 +984,19 @@ function openDbReadOnly(dbPath) {
|
|
|
982
984
|
return null;
|
|
983
985
|
}
|
|
984
986
|
}
|
|
987
|
+
function openDb(dbPath) {
|
|
988
|
+
if (!DatabaseConstructor) return null;
|
|
989
|
+
try {
|
|
990
|
+
mkdirSync(dirname2(dbPath), { recursive: true });
|
|
991
|
+
const db = DatabaseConstructor(dbPath);
|
|
992
|
+
db.pragma("journal_mode = WAL");
|
|
993
|
+
db.pragma("synchronous = NORMAL");
|
|
994
|
+
db.pragma("foreign_keys = ON");
|
|
995
|
+
return db;
|
|
996
|
+
} catch {
|
|
997
|
+
return null;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
985
1000
|
function isSqliteAvailable() {
|
|
986
1001
|
return DatabaseConstructor !== null;
|
|
987
1002
|
}
|
|
@@ -1340,7 +1355,7 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1340
1355
|
parseSessionDir(sessionDir) {
|
|
1341
1356
|
try {
|
|
1342
1357
|
const sessionId = basename3(sessionDir);
|
|
1343
|
-
const projectHash = basename3(
|
|
1358
|
+
const projectHash = basename3(dirname3(sessionDir));
|
|
1344
1359
|
const contextFile = join4(sessionDir, "context.jsonl");
|
|
1345
1360
|
const wireFile = join4(sessionDir, "wire.jsonl");
|
|
1346
1361
|
if (!existsSync4(contextFile) && !existsSync4(wireFile)) return null;
|
|
@@ -3492,89 +3507,438 @@ registerAgent({
|
|
|
3492
3507
|
icon: "/icon/agent/cursor.svg",
|
|
3493
3508
|
create: () => new CursorAgent()
|
|
3494
3509
|
});
|
|
3495
|
-
var CACHE_VERSION =
|
|
3496
|
-
var
|
|
3510
|
+
var CACHE_VERSION = 3;
|
|
3511
|
+
var CACHE_TTL = 7 * 24 * 60 * 60 * 1e3;
|
|
3512
|
+
var CACHE_FILENAME = "codesesh.db";
|
|
3513
|
+
var LEGACY_CACHE_FILENAME = "scan-cache.json";
|
|
3514
|
+
function getCacheDir() {
|
|
3515
|
+
return join7(homedir2(), ".cache", "codesesh");
|
|
3516
|
+
}
|
|
3497
3517
|
function getCachePath() {
|
|
3498
|
-
return join7(
|
|
3518
|
+
return join7(getCacheDir(), CACHE_FILENAME);
|
|
3499
3519
|
}
|
|
3500
|
-
function
|
|
3501
|
-
|
|
3502
|
-
if (!existsSync7(cacheDir)) {
|
|
3503
|
-
mkdirSync(cacheDir, { recursive: true });
|
|
3504
|
-
}
|
|
3520
|
+
function getLegacyCachePath() {
|
|
3521
|
+
return join7(getCacheDir(), LEGACY_CACHE_FILENAME);
|
|
3505
3522
|
}
|
|
3506
|
-
function
|
|
3523
|
+
function hasCacheStorage() {
|
|
3524
|
+
return existsSync7(getCachePath());
|
|
3525
|
+
}
|
|
3526
|
+
function withCacheDb(fn) {
|
|
3527
|
+
const db = openDb(getCachePath());
|
|
3528
|
+
if (!db) return null;
|
|
3507
3529
|
try {
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
const data = JSON.parse(readFileSync6(cachePath, "utf-8"));
|
|
3511
|
-
if (data.version !== CACHE_VERSION) return null;
|
|
3512
|
-
const entry = data.entries[agentName];
|
|
3513
|
-
if (!entry) return null;
|
|
3514
|
-
const CACHE_TTL = 7 * 24 * 60 * 60 * 1e3;
|
|
3515
|
-
if (Date.now() - entry.timestamp > CACHE_TTL) return null;
|
|
3516
|
-
return { sessions: entry.sessions, meta: entry.meta || {}, timestamp: entry.timestamp };
|
|
3530
|
+
ensureSchema(db);
|
|
3531
|
+
return fn(db);
|
|
3517
3532
|
} catch {
|
|
3518
3533
|
return null;
|
|
3534
|
+
} finally {
|
|
3535
|
+
db.close();
|
|
3519
3536
|
}
|
|
3520
3537
|
}
|
|
3521
|
-
function
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3538
|
+
function ensureSchema(db) {
|
|
3539
|
+
db.exec(`
|
|
3540
|
+
CREATE TABLE IF NOT EXISTS cache_meta (
|
|
3541
|
+
key TEXT PRIMARY KEY,
|
|
3542
|
+
value TEXT NOT NULL
|
|
3543
|
+
);
|
|
3544
|
+
|
|
3545
|
+
CREATE TABLE IF NOT EXISTS agent_cache (
|
|
3546
|
+
agent_name TEXT PRIMARY KEY,
|
|
3547
|
+
timestamp INTEGER NOT NULL
|
|
3548
|
+
);
|
|
3549
|
+
|
|
3550
|
+
CREATE TABLE IF NOT EXISTS cached_sessions (
|
|
3551
|
+
agent_name TEXT NOT NULL,
|
|
3552
|
+
session_id TEXT NOT NULL,
|
|
3553
|
+
session_json TEXT NOT NULL,
|
|
3554
|
+
meta_json TEXT,
|
|
3555
|
+
PRIMARY KEY (agent_name, session_id)
|
|
3556
|
+
);
|
|
3557
|
+
|
|
3558
|
+
CREATE TABLE IF NOT EXISTS session_documents (
|
|
3559
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
3560
|
+
agent_name TEXT NOT NULL,
|
|
3561
|
+
session_id TEXT NOT NULL,
|
|
3562
|
+
slug TEXT NOT NULL,
|
|
3563
|
+
title TEXT NOT NULL,
|
|
3564
|
+
directory TEXT NOT NULL,
|
|
3565
|
+
time_created INTEGER NOT NULL,
|
|
3566
|
+
time_updated INTEGER,
|
|
3567
|
+
activity_time INTEGER NOT NULL,
|
|
3568
|
+
content_text TEXT NOT NULL,
|
|
3569
|
+
content_hash TEXT NOT NULL,
|
|
3570
|
+
indexed_at INTEGER NOT NULL,
|
|
3571
|
+
UNIQUE(agent_name, session_id)
|
|
3572
|
+
);
|
|
3573
|
+
|
|
3574
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS session_documents_fts USING fts5(
|
|
3575
|
+
title,
|
|
3576
|
+
content_text,
|
|
3577
|
+
content='session_documents',
|
|
3578
|
+
content_rowid='id'
|
|
3579
|
+
);
|
|
3580
|
+
|
|
3581
|
+
CREATE TRIGGER IF NOT EXISTS session_documents_ai AFTER INSERT ON session_documents BEGIN
|
|
3582
|
+
INSERT INTO session_documents_fts(rowid, title, content_text)
|
|
3583
|
+
VALUES (new.id, new.title, new.content_text);
|
|
3584
|
+
END;
|
|
3585
|
+
|
|
3586
|
+
CREATE TRIGGER IF NOT EXISTS session_documents_ad AFTER DELETE ON session_documents BEGIN
|
|
3587
|
+
INSERT INTO session_documents_fts(session_documents_fts, rowid, title, content_text)
|
|
3588
|
+
VALUES ('delete', old.id, old.title, old.content_text);
|
|
3589
|
+
END;
|
|
3590
|
+
|
|
3591
|
+
CREATE TRIGGER IF NOT EXISTS session_documents_au AFTER UPDATE ON session_documents BEGIN
|
|
3592
|
+
INSERT INTO session_documents_fts(session_documents_fts, rowid, title, content_text)
|
|
3593
|
+
VALUES ('delete', old.id, old.title, old.content_text);
|
|
3594
|
+
INSERT INTO session_documents_fts(rowid, title, content_text)
|
|
3595
|
+
VALUES (new.id, new.title, new.content_text);
|
|
3596
|
+
END;
|
|
3597
|
+
`);
|
|
3598
|
+
const versionRow = db.prepare("SELECT value FROM cache_meta WHERE key = 'version'").get();
|
|
3599
|
+
const version = Number(versionRow?.value ?? 0);
|
|
3600
|
+
if (version === CACHE_VERSION) {
|
|
3601
|
+
return;
|
|
3602
|
+
}
|
|
3603
|
+
db.exec(`
|
|
3604
|
+
DELETE FROM agent_cache;
|
|
3605
|
+
DELETE FROM cached_sessions;
|
|
3606
|
+
DELETE FROM session_documents;
|
|
3607
|
+
INSERT INTO session_documents_fts(session_documents_fts) VALUES ('rebuild');
|
|
3608
|
+
INSERT INTO cache_meta(key, value)
|
|
3609
|
+
VALUES ('version', '${CACHE_VERSION}')
|
|
3610
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value;
|
|
3611
|
+
`);
|
|
3612
|
+
}
|
|
3613
|
+
function sessionContentHash(session) {
|
|
3614
|
+
return JSON.stringify([
|
|
3615
|
+
session.slug,
|
|
3616
|
+
session.title,
|
|
3617
|
+
session.directory,
|
|
3618
|
+
session.time_created,
|
|
3619
|
+
session.time_updated ?? session.time_created,
|
|
3620
|
+
session.stats.message_count,
|
|
3621
|
+
session.stats.total_input_tokens,
|
|
3622
|
+
session.stats.total_output_tokens,
|
|
3623
|
+
session.stats.total_cost,
|
|
3624
|
+
session.stats.total_tokens ?? 0
|
|
3625
|
+
]);
|
|
3626
|
+
}
|
|
3627
|
+
function escapeFtsTerm(value) {
|
|
3628
|
+
return value.replaceAll('"', '""');
|
|
3629
|
+
}
|
|
3630
|
+
function toFtsQuery(input) {
|
|
3631
|
+
const tokens = input.match(/"[^"]+"|\S+/g) ?? [];
|
|
3632
|
+
return tokens.map((token) => {
|
|
3633
|
+
if (/^OR$/i.test(token)) {
|
|
3634
|
+
return "OR";
|
|
3537
3635
|
}
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3636
|
+
if (token.startsWith('"') && token.endsWith('"')) {
|
|
3637
|
+
return `"${escapeFtsTerm(token.slice(1, -1))}"`;
|
|
3638
|
+
}
|
|
3639
|
+
return `"${escapeFtsTerm(token)}"`;
|
|
3640
|
+
}).join(" ");
|
|
3641
|
+
}
|
|
3642
|
+
function appendPlainText(value, chunks) {
|
|
3643
|
+
if (value == null) return;
|
|
3644
|
+
if (typeof value === "string") {
|
|
3645
|
+
const normalized = value.trim();
|
|
3646
|
+
if (normalized) {
|
|
3647
|
+
chunks.push(normalized);
|
|
3648
|
+
}
|
|
3649
|
+
return;
|
|
3650
|
+
}
|
|
3651
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
3652
|
+
chunks.push(String(value));
|
|
3653
|
+
return;
|
|
3654
|
+
}
|
|
3655
|
+
if (Array.isArray(value)) {
|
|
3656
|
+
for (const item of value) {
|
|
3657
|
+
appendPlainText(item, chunks);
|
|
3658
|
+
}
|
|
3659
|
+
return;
|
|
3660
|
+
}
|
|
3661
|
+
if (typeof value === "object") {
|
|
3662
|
+
for (const nested of Object.values(value)) {
|
|
3663
|
+
appendPlainText(nested, chunks);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
function buildSessionContent(session) {
|
|
3668
|
+
const chunks = [];
|
|
3669
|
+
appendPlainText(session.title, chunks);
|
|
3670
|
+
for (const message of session.messages) {
|
|
3671
|
+
chunks.push(message.role);
|
|
3672
|
+
appendPlainText(message.agent, chunks);
|
|
3673
|
+
appendPlainText(message.model, chunks);
|
|
3674
|
+
for (const part of message.parts) {
|
|
3675
|
+
appendPlainText(part.type, chunks);
|
|
3676
|
+
appendPlainText(part.title, chunks);
|
|
3677
|
+
appendPlainText(part.nickname, chunks);
|
|
3678
|
+
appendPlainText(part.tool, chunks);
|
|
3679
|
+
appendPlainText(part.text, chunks);
|
|
3680
|
+
appendPlainText(part.input, chunks);
|
|
3681
|
+
appendPlainText(part.output, chunks);
|
|
3682
|
+
appendPlainText(part.state, chunks);
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
return chunks.join("\n");
|
|
3686
|
+
}
|
|
3687
|
+
function deleteLegacyCacheFile() {
|
|
3688
|
+
const legacyPath = getLegacyCachePath();
|
|
3689
|
+
if (!existsSync7(legacyPath)) {
|
|
3690
|
+
return;
|
|
3691
|
+
}
|
|
3692
|
+
try {
|
|
3693
|
+
unlinkSync(legacyPath);
|
|
3546
3694
|
} catch {
|
|
3547
3695
|
}
|
|
3548
3696
|
}
|
|
3697
|
+
function loadCachedSessions(agentName) {
|
|
3698
|
+
if (!hasCacheStorage()) {
|
|
3699
|
+
return null;
|
|
3700
|
+
}
|
|
3701
|
+
return withCacheDb((db) => {
|
|
3702
|
+
const timestampRow = db.prepare("SELECT timestamp AS value FROM agent_cache WHERE agent_name = ?").get(agentName);
|
|
3703
|
+
const timestamp = Number(timestampRow?.value ?? 0);
|
|
3704
|
+
if (!timestamp || Date.now() - timestamp > CACHE_TTL) {
|
|
3705
|
+
return null;
|
|
3706
|
+
}
|
|
3707
|
+
const rows = db.prepare(
|
|
3708
|
+
`
|
|
3709
|
+
SELECT session_json, meta_json
|
|
3710
|
+
FROM cached_sessions
|
|
3711
|
+
WHERE agent_name = ?
|
|
3712
|
+
ORDER BY rowid
|
|
3713
|
+
`
|
|
3714
|
+
).all(agentName);
|
|
3715
|
+
const sessions = [];
|
|
3716
|
+
const meta = {};
|
|
3717
|
+
for (const row of rows) {
|
|
3718
|
+
if (!row.session_json) {
|
|
3719
|
+
continue;
|
|
3720
|
+
}
|
|
3721
|
+
const session = JSON.parse(row.session_json);
|
|
3722
|
+
sessions.push(session);
|
|
3723
|
+
if (row.meta_json) {
|
|
3724
|
+
meta[session.id] = JSON.parse(row.meta_json);
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
return { sessions, meta, timestamp };
|
|
3728
|
+
});
|
|
3729
|
+
}
|
|
3730
|
+
function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
3731
|
+
withCacheDb((db) => {
|
|
3732
|
+
const deleteAgent = db.prepare("DELETE FROM agent_cache WHERE agent_name = ?");
|
|
3733
|
+
const deleteSessions = db.prepare("DELETE FROM cached_sessions WHERE agent_name = ?");
|
|
3734
|
+
const upsertAgent = db.prepare(`
|
|
3735
|
+
INSERT INTO agent_cache(agent_name, timestamp)
|
|
3736
|
+
VALUES (?, ?)
|
|
3737
|
+
ON CONFLICT(agent_name) DO UPDATE SET timestamp = excluded.timestamp
|
|
3738
|
+
`);
|
|
3739
|
+
const insertSession = db.prepare(`
|
|
3740
|
+
INSERT INTO cached_sessions(agent_name, session_id, session_json, meta_json)
|
|
3741
|
+
VALUES (?, ?, ?, ?)
|
|
3742
|
+
`);
|
|
3743
|
+
const write = db.transaction(() => {
|
|
3744
|
+
const timestamp = Date.now();
|
|
3745
|
+
deleteAgent.run(agentName);
|
|
3746
|
+
deleteSessions.run(agentName);
|
|
3747
|
+
upsertAgent.run(agentName, timestamp);
|
|
3748
|
+
for (const session of sessions) {
|
|
3749
|
+
insertSession.run(
|
|
3750
|
+
agentName,
|
|
3751
|
+
session.id,
|
|
3752
|
+
JSON.stringify(session),
|
|
3753
|
+
meta[session.id] ? JSON.stringify(meta[session.id]) : null
|
|
3754
|
+
);
|
|
3755
|
+
}
|
|
3756
|
+
});
|
|
3757
|
+
write();
|
|
3758
|
+
deleteLegacyCacheFile();
|
|
3759
|
+
});
|
|
3760
|
+
}
|
|
3549
3761
|
function clearCache() {
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3762
|
+
if (!hasCacheStorage()) {
|
|
3763
|
+
deleteLegacyCacheFile();
|
|
3764
|
+
return;
|
|
3765
|
+
}
|
|
3766
|
+
withCacheDb((db) => {
|
|
3767
|
+
db.exec(`
|
|
3768
|
+
DELETE FROM agent_cache;
|
|
3769
|
+
DELETE FROM cached_sessions;
|
|
3770
|
+
`);
|
|
3771
|
+
});
|
|
3772
|
+
deleteLegacyCacheFile();
|
|
3773
|
+
const cachePath = getCachePath();
|
|
3774
|
+
const walPath = `${cachePath}-wal`;
|
|
3775
|
+
const shmPath = `${cachePath}-shm`;
|
|
3776
|
+
for (const filePath of [walPath, shmPath]) {
|
|
3777
|
+
if (!existsSync7(filePath)) {
|
|
3778
|
+
continue;
|
|
3779
|
+
}
|
|
3780
|
+
try {
|
|
3781
|
+
rmSync(filePath, { force: true });
|
|
3782
|
+
} catch {
|
|
3559
3783
|
}
|
|
3560
|
-
} catch {
|
|
3561
3784
|
}
|
|
3562
3785
|
}
|
|
3563
3786
|
function getCacheInfo() {
|
|
3564
|
-
|
|
3565
|
-
const cachePath = getCachePath();
|
|
3566
|
-
if (!existsSync7(cachePath)) {
|
|
3567
|
-
return { lastScanTime: null, size: 0 };
|
|
3568
|
-
}
|
|
3569
|
-
const data = JSON.parse(readFileSync6(cachePath, "utf-8"));
|
|
3570
|
-
const size = Object.values(data.entries).reduce((sum, entry) => sum + entry.sessions.length, 0);
|
|
3571
|
-
return {
|
|
3572
|
-
lastScanTime: data.lastScanTime || null,
|
|
3573
|
-
size
|
|
3574
|
-
};
|
|
3575
|
-
} catch {
|
|
3787
|
+
if (!hasCacheStorage()) {
|
|
3576
3788
|
return { lastScanTime: null, size: 0 };
|
|
3577
3789
|
}
|
|
3790
|
+
const info = withCacheDb((db) => {
|
|
3791
|
+
const timestampRow = db.prepare("SELECT MAX(timestamp) AS value FROM agent_cache").get();
|
|
3792
|
+
const sizeRow = db.prepare("SELECT COUNT(*) AS value FROM cached_sessions").get();
|
|
3793
|
+
const lastScanTime = Number(timestampRow?.value ?? 0) || null;
|
|
3794
|
+
const size = Number(sizeRow?.value ?? 0);
|
|
3795
|
+
return { lastScanTime, size };
|
|
3796
|
+
});
|
|
3797
|
+
return info ?? { lastScanTime: null, size: 0 };
|
|
3798
|
+
}
|
|
3799
|
+
function syncSessionSearchIndex(agentName, sessions, loadSessionData) {
|
|
3800
|
+
if (!hasCacheStorage()) {
|
|
3801
|
+
return;
|
|
3802
|
+
}
|
|
3803
|
+
withCacheDb((db) => {
|
|
3804
|
+
const existingRows = db.prepare(
|
|
3805
|
+
"SELECT session_id, content_hash FROM session_documents WHERE agent_name = ? ORDER BY id"
|
|
3806
|
+
).all(agentName);
|
|
3807
|
+
const existingMap = new Map(
|
|
3808
|
+
existingRows.map((row) => [String(row.session_id), String(row.content_hash ?? "")])
|
|
3809
|
+
);
|
|
3810
|
+
const sessionMap = new Map(sessions.map((session) => [session.id, session]));
|
|
3811
|
+
const toDelete = existingRows.map((row) => String(row.session_id)).filter((sessionId) => !sessionMap.has(sessionId));
|
|
3812
|
+
const toUpsert = sessions.filter(
|
|
3813
|
+
(session) => existingMap.get(session.id) !== sessionContentHash(session)
|
|
3814
|
+
);
|
|
3815
|
+
const loaded = toUpsert.map((session) => {
|
|
3816
|
+
try {
|
|
3817
|
+
const data = loadSessionData(session.id);
|
|
3818
|
+
return {
|
|
3819
|
+
session,
|
|
3820
|
+
contentText: buildSessionContent(data),
|
|
3821
|
+
contentHash: sessionContentHash(session)
|
|
3822
|
+
};
|
|
3823
|
+
} catch {
|
|
3824
|
+
return null;
|
|
3825
|
+
}
|
|
3826
|
+
}).filter((entry) => entry !== null);
|
|
3827
|
+
const deleteRow = db.prepare(
|
|
3828
|
+
"DELETE FROM session_documents WHERE agent_name = ? AND session_id = ?"
|
|
3829
|
+
);
|
|
3830
|
+
const upsertRow = db.prepare(`
|
|
3831
|
+
INSERT INTO session_documents(
|
|
3832
|
+
agent_name,
|
|
3833
|
+
session_id,
|
|
3834
|
+
slug,
|
|
3835
|
+
title,
|
|
3836
|
+
directory,
|
|
3837
|
+
time_created,
|
|
3838
|
+
time_updated,
|
|
3839
|
+
activity_time,
|
|
3840
|
+
content_text,
|
|
3841
|
+
content_hash,
|
|
3842
|
+
indexed_at
|
|
3843
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3844
|
+
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
3845
|
+
slug = excluded.slug,
|
|
3846
|
+
title = excluded.title,
|
|
3847
|
+
directory = excluded.directory,
|
|
3848
|
+
time_created = excluded.time_created,
|
|
3849
|
+
time_updated = excluded.time_updated,
|
|
3850
|
+
activity_time = excluded.activity_time,
|
|
3851
|
+
content_text = excluded.content_text,
|
|
3852
|
+
content_hash = excluded.content_hash,
|
|
3853
|
+
indexed_at = excluded.indexed_at
|
|
3854
|
+
`);
|
|
3855
|
+
const write = db.transaction(() => {
|
|
3856
|
+
for (const sessionId of toDelete) {
|
|
3857
|
+
deleteRow.run(agentName, sessionId);
|
|
3858
|
+
}
|
|
3859
|
+
for (const entry of loaded) {
|
|
3860
|
+
const activityTime = entry.session.time_updated ?? entry.session.time_created;
|
|
3861
|
+
upsertRow.run(
|
|
3862
|
+
agentName,
|
|
3863
|
+
entry.session.id,
|
|
3864
|
+
entry.session.slug,
|
|
3865
|
+
entry.session.title,
|
|
3866
|
+
entry.session.directory,
|
|
3867
|
+
entry.session.time_created,
|
|
3868
|
+
entry.session.time_updated ?? null,
|
|
3869
|
+
activityTime,
|
|
3870
|
+
entry.contentText,
|
|
3871
|
+
entry.contentHash,
|
|
3872
|
+
Date.now()
|
|
3873
|
+
);
|
|
3874
|
+
}
|
|
3875
|
+
});
|
|
3876
|
+
write();
|
|
3877
|
+
});
|
|
3878
|
+
}
|
|
3879
|
+
function searchSessions(query, options = {}) {
|
|
3880
|
+
const normalizedQuery = query.trim();
|
|
3881
|
+
if (!normalizedQuery || !hasCacheStorage()) {
|
|
3882
|
+
return [];
|
|
3883
|
+
}
|
|
3884
|
+
const ftsQuery = toFtsQuery(normalizedQuery);
|
|
3885
|
+
const results = withCacheDb((db) => {
|
|
3886
|
+
const rows = db.prepare(
|
|
3887
|
+
`
|
|
3888
|
+
SELECT
|
|
3889
|
+
d.agent_name,
|
|
3890
|
+
d.session_id,
|
|
3891
|
+
d.slug,
|
|
3892
|
+
d.title,
|
|
3893
|
+
d.directory,
|
|
3894
|
+
d.time_created,
|
|
3895
|
+
d.time_updated,
|
|
3896
|
+
COALESCE(
|
|
3897
|
+
NULLIF(snippet(session_documents_fts, 1, '<mark>', '</mark>', ' \u2026 ', 18), ''),
|
|
3898
|
+
highlight(session_documents_fts, 0, '<mark>', '</mark>')
|
|
3899
|
+
) AS snippet
|
|
3900
|
+
FROM session_documents_fts
|
|
3901
|
+
JOIN session_documents d ON d.id = session_documents_fts.rowid
|
|
3902
|
+
WHERE session_documents_fts MATCH ?
|
|
3903
|
+
AND (? IS NULL OR d.agent_name = ?)
|
|
3904
|
+
AND (? IS NULL OR LOWER(d.directory) LIKE ?)
|
|
3905
|
+
AND (? IS NULL OR d.activity_time >= ?)
|
|
3906
|
+
AND (? IS NULL OR d.activity_time <= ?)
|
|
3907
|
+
ORDER BY bm25(session_documents_fts, 8.0, 1.0), d.activity_time DESC
|
|
3908
|
+
LIMIT ?
|
|
3909
|
+
`
|
|
3910
|
+
).all(
|
|
3911
|
+
ftsQuery,
|
|
3912
|
+
options.agent ?? null,
|
|
3913
|
+
options.agent ?? null,
|
|
3914
|
+
options.cwd?.toLowerCase() ?? null,
|
|
3915
|
+
options.cwd ? `%${options.cwd.toLowerCase()}%` : null,
|
|
3916
|
+
options.from ?? null,
|
|
3917
|
+
options.from ?? null,
|
|
3918
|
+
options.to ?? null,
|
|
3919
|
+
options.to ?? null,
|
|
3920
|
+
options.limit ?? 50
|
|
3921
|
+
);
|
|
3922
|
+
return rows.map((row) => ({
|
|
3923
|
+
agentName: String(row.agent_name),
|
|
3924
|
+
session: {
|
|
3925
|
+
id: String(row.session_id),
|
|
3926
|
+
slug: String(row.slug),
|
|
3927
|
+
title: String(row.title),
|
|
3928
|
+
directory: String(row.directory),
|
|
3929
|
+
time_created: Number(row.time_created),
|
|
3930
|
+
time_updated: row.time_updated == null ? void 0 : Number(row.time_updated),
|
|
3931
|
+
stats: {
|
|
3932
|
+
message_count: 0,
|
|
3933
|
+
total_input_tokens: 0,
|
|
3934
|
+
total_output_tokens: 0,
|
|
3935
|
+
total_cost: 0
|
|
3936
|
+
}
|
|
3937
|
+
},
|
|
3938
|
+
snippet: String(row.snippet ?? "")
|
|
3939
|
+
}));
|
|
3940
|
+
});
|
|
3941
|
+
return results ?? [];
|
|
3578
3942
|
}
|
|
3579
3943
|
function isPathScopeMatch(queryPath, sessionPath) {
|
|
3580
3944
|
if (!sessionPath) return false;
|
|
@@ -3728,13 +4092,16 @@ export {
|
|
|
3728
4092
|
resolveSessionTitle,
|
|
3729
4093
|
perf,
|
|
3730
4094
|
openDbReadOnly,
|
|
4095
|
+
openDb,
|
|
3731
4096
|
isSqliteAvailable,
|
|
3732
4097
|
loadCachedSessions,
|
|
3733
4098
|
saveCachedSessions,
|
|
3734
4099
|
clearCache,
|
|
3735
4100
|
getCacheInfo,
|
|
4101
|
+
syncSessionSearchIndex,
|
|
4102
|
+
searchSessions,
|
|
3736
4103
|
filterSessions,
|
|
3737
4104
|
scanSessions,
|
|
3738
4105
|
scanSessionsAsync
|
|
3739
4106
|
};
|
|
3740
|
-
//# sourceMappingURL=chunk-
|
|
4107
|
+
//# sourceMappingURL=chunk-UQI7CTEK.js.map
|