codesesh 0.2.0 → 0.4.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 +11 -6
- package/dist/{chunk-2UNXB2D3.js → chunk-EIIG7J6V.js} +705 -77
- package/dist/chunk-EIIG7J6V.js.map +1 -0
- package/dist/{dist-6EV6SS6N.js → dist-2YXXOCZJ.js} +18 -4
- package/dist/index.js +131 -20
- package/dist/index.js.map +1 -1
- package/dist/web/assets/index-DJuE5oTj.js +68 -0
- package/dist/web/assets/index-gSYgPU_H.css +2 -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-2YXXOCZJ.js.map} +0 -0
|
@@ -10,18 +10,22 @@ 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";
|
|
27
|
+
import { homedir as homedir3, platform as platform2 } from "os";
|
|
28
|
+
import { join as join8 } from "path";
|
|
25
29
|
var registrations = [];
|
|
26
30
|
function registerAgent(reg) {
|
|
27
31
|
registrations.push(reg);
|
|
@@ -306,6 +310,8 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
306
310
|
let totalCost = 0;
|
|
307
311
|
let totalInputTokens = 0;
|
|
308
312
|
let totalOutputTokens = 0;
|
|
313
|
+
let totalCacheRead = 0;
|
|
314
|
+
let totalCacheCreate = 0;
|
|
309
315
|
for (const record of parseJsonlLines(content)) {
|
|
310
316
|
try {
|
|
311
317
|
this.convertRecord(
|
|
@@ -323,6 +329,8 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
323
329
|
totalCost += msg.cost ?? 0;
|
|
324
330
|
totalInputTokens += msg.tokens?.input ?? 0;
|
|
325
331
|
totalOutputTokens += msg.tokens?.output ?? 0;
|
|
332
|
+
totalCacheRead += msg.tokens?.cache_read ?? 0;
|
|
333
|
+
totalCacheCreate += msg.tokens?.cache_create ?? 0;
|
|
326
334
|
}
|
|
327
335
|
return {
|
|
328
336
|
id: meta.id,
|
|
@@ -336,7 +344,9 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
336
344
|
message_count: messages.length,
|
|
337
345
|
total_input_tokens: totalInputTokens,
|
|
338
346
|
total_output_tokens: totalOutputTokens,
|
|
339
|
-
total_cost: totalCost
|
|
347
|
+
total_cost: totalCost,
|
|
348
|
+
total_cache_read_tokens: totalCacheRead,
|
|
349
|
+
total_cache_create_tokens: totalCacheCreate
|
|
340
350
|
},
|
|
341
351
|
messages
|
|
342
352
|
};
|
|
@@ -510,6 +520,9 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
510
520
|
let cwd = null;
|
|
511
521
|
let totalInputTokens = 0;
|
|
512
522
|
let totalOutputTokens = 0;
|
|
523
|
+
let totalCacheReadTokens = 0;
|
|
524
|
+
let totalCacheCreateTokens = 0;
|
|
525
|
+
const modelUsageMap = {};
|
|
513
526
|
for (const line of lines) {
|
|
514
527
|
try {
|
|
515
528
|
const data = JSON.parse(line);
|
|
@@ -531,8 +544,20 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
531
544
|
if (role === "assistant") {
|
|
532
545
|
const usage = msg["usage"];
|
|
533
546
|
if (usage && typeof usage === "object") {
|
|
534
|
-
|
|
535
|
-
|
|
547
|
+
const inputTokens = usage["input_tokens"] ?? 0;
|
|
548
|
+
const cacheRead = usage["cache_read_input_tokens"] ?? 0;
|
|
549
|
+
const cacheCreate = usage["cache_creation_input_tokens"] ?? 0;
|
|
550
|
+
const outputTokens = usage["output_tokens"] ?? 0;
|
|
551
|
+
totalInputTokens += inputTokens + cacheRead + cacheCreate;
|
|
552
|
+
totalOutputTokens += outputTokens;
|
|
553
|
+
totalCacheReadTokens += cacheRead;
|
|
554
|
+
totalCacheCreateTokens += cacheCreate;
|
|
555
|
+
const m = msg["model"];
|
|
556
|
+
if (typeof m === "string" && m.trim()) {
|
|
557
|
+
const name = m.trim();
|
|
558
|
+
const msgTotal = inputTokens + cacheRead + cacheCreate + outputTokens;
|
|
559
|
+
modelUsageMap[name] = (modelUsageMap[name] ?? 0) + msgTotal;
|
|
560
|
+
}
|
|
536
561
|
}
|
|
537
562
|
}
|
|
538
563
|
}
|
|
@@ -543,6 +568,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
543
568
|
const messageTitle = this.extractTitle(lines);
|
|
544
569
|
const directoryTitle = basenameTitle(directory) || basenameTitle(projectDir);
|
|
545
570
|
const title = resolveSessionTitle(explicitTitle, messageTitle, directoryTitle);
|
|
571
|
+
const hasModelUsage = Object.keys(modelUsageMap).length > 0;
|
|
546
572
|
return {
|
|
547
573
|
id: sessionId,
|
|
548
574
|
slug: `claudecode/${sessionId}`,
|
|
@@ -554,8 +580,11 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
554
580
|
message_count: messageCount,
|
|
555
581
|
total_input_tokens: totalInputTokens,
|
|
556
582
|
total_output_tokens: totalOutputTokens,
|
|
557
|
-
total_cost: 0
|
|
558
|
-
|
|
583
|
+
total_cost: 0,
|
|
584
|
+
total_cache_read_tokens: totalCacheReadTokens,
|
|
585
|
+
total_cache_create_tokens: totalCacheCreateTokens
|
|
586
|
+
},
|
|
587
|
+
model_usage: hasModelUsage ? modelUsageMap : void 0
|
|
559
588
|
};
|
|
560
589
|
}
|
|
561
590
|
extractTitle(lines) {
|
|
@@ -783,9 +812,13 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
783
812
|
const usage = msg["usage"];
|
|
784
813
|
if (usage && typeof usage === "object" && !message.tokens) {
|
|
785
814
|
const u = usage;
|
|
815
|
+
const cacheRead = u["cache_read_input_tokens"] ?? 0;
|
|
816
|
+
const cacheCreate = u["cache_creation_input_tokens"] ?? 0;
|
|
786
817
|
message.tokens = {
|
|
787
|
-
input: (u["input_tokens"] ?? 0) +
|
|
788
|
-
output: u["output_tokens"] ?? 0
|
|
818
|
+
input: (u["input_tokens"] ?? 0) + cacheCreate + cacheRead,
|
|
819
|
+
output: u["output_tokens"] ?? 0,
|
|
820
|
+
cache_read: cacheRead,
|
|
821
|
+
cache_create: cacheCreate
|
|
789
822
|
};
|
|
790
823
|
}
|
|
791
824
|
}
|
|
@@ -982,6 +1015,19 @@ function openDbReadOnly(dbPath) {
|
|
|
982
1015
|
return null;
|
|
983
1016
|
}
|
|
984
1017
|
}
|
|
1018
|
+
function openDb(dbPath) {
|
|
1019
|
+
if (!DatabaseConstructor) return null;
|
|
1020
|
+
try {
|
|
1021
|
+
mkdirSync(dirname2(dbPath), { recursive: true });
|
|
1022
|
+
const db = DatabaseConstructor(dbPath);
|
|
1023
|
+
db.pragma("journal_mode = WAL");
|
|
1024
|
+
db.pragma("synchronous = NORMAL");
|
|
1025
|
+
db.pragma("foreign_keys = ON");
|
|
1026
|
+
return db;
|
|
1027
|
+
} catch {
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
985
1031
|
function isSqliteAvailable() {
|
|
986
1032
|
return DatabaseConstructor !== null;
|
|
987
1033
|
}
|
|
@@ -1340,7 +1386,7 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1340
1386
|
parseSessionDir(sessionDir) {
|
|
1341
1387
|
try {
|
|
1342
1388
|
const sessionId = basename3(sessionDir);
|
|
1343
|
-
const projectHash = basename3(
|
|
1389
|
+
const projectHash = basename3(dirname3(sessionDir));
|
|
1344
1390
|
const contextFile = join4(sessionDir, "context.jsonl");
|
|
1345
1391
|
const wireFile = join4(sessionDir, "wire.jsonl");
|
|
1346
1392
|
if (!existsSync4(contextFile) && !existsSync4(wireFile)) return null;
|
|
@@ -3072,6 +3118,16 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3072
3118
|
}
|
|
3073
3119
|
const messageCount = messages.length;
|
|
3074
3120
|
const directory = workspacePathMap.get(composerId) ?? "";
|
|
3121
|
+
const modelUsageMap = {};
|
|
3122
|
+
for (const msg of messages) {
|
|
3123
|
+
if (msg.model) {
|
|
3124
|
+
const msgTokens = (msg.tokens?.input ?? 0) + (msg.tokens?.output ?? 0);
|
|
3125
|
+
if (msgTokens > 0) {
|
|
3126
|
+
modelUsageMap[msg.model] = (modelUsageMap[msg.model] ?? 0) + msgTokens;
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
const hasModelUsage = Object.keys(modelUsageMap).length > 0;
|
|
3075
3131
|
heads.push({
|
|
3076
3132
|
id: sessionId,
|
|
3077
3133
|
slug: `cursor/${sessionId}`,
|
|
@@ -3084,7 +3140,8 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3084
3140
|
total_input_tokens: composer.inputTokenCount ?? 0,
|
|
3085
3141
|
total_output_tokens: composer.outputTokenCount ?? 0,
|
|
3086
3142
|
total_cost: 0
|
|
3087
|
-
}
|
|
3143
|
+
},
|
|
3144
|
+
model_usage: hasModelUsage ? modelUsageMap : void 0
|
|
3088
3145
|
});
|
|
3089
3146
|
this.composerCache.set(sessionId, composer);
|
|
3090
3147
|
this.composerCache.set(`__mapping__${composerId}`, {
|
|
@@ -3492,89 +3549,438 @@ registerAgent({
|
|
|
3492
3549
|
icon: "/icon/agent/cursor.svg",
|
|
3493
3550
|
create: () => new CursorAgent()
|
|
3494
3551
|
});
|
|
3495
|
-
var CACHE_VERSION =
|
|
3496
|
-
var
|
|
3552
|
+
var CACHE_VERSION = 4;
|
|
3553
|
+
var CACHE_TTL = 7 * 24 * 60 * 60 * 1e3;
|
|
3554
|
+
var CACHE_FILENAME = "codesesh.db";
|
|
3555
|
+
var LEGACY_CACHE_FILENAME = "scan-cache.json";
|
|
3556
|
+
function getCacheDir() {
|
|
3557
|
+
return join7(homedir2(), ".cache", "codesesh");
|
|
3558
|
+
}
|
|
3497
3559
|
function getCachePath() {
|
|
3498
|
-
return join7(
|
|
3560
|
+
return join7(getCacheDir(), CACHE_FILENAME);
|
|
3499
3561
|
}
|
|
3500
|
-
function
|
|
3501
|
-
|
|
3502
|
-
if (!existsSync7(cacheDir)) {
|
|
3503
|
-
mkdirSync(cacheDir, { recursive: true });
|
|
3504
|
-
}
|
|
3562
|
+
function getLegacyCachePath() {
|
|
3563
|
+
return join7(getCacheDir(), LEGACY_CACHE_FILENAME);
|
|
3505
3564
|
}
|
|
3506
|
-
function
|
|
3565
|
+
function hasCacheStorage() {
|
|
3566
|
+
return existsSync7(getCachePath());
|
|
3567
|
+
}
|
|
3568
|
+
function withCacheDb(fn) {
|
|
3569
|
+
const db = openDb(getCachePath());
|
|
3570
|
+
if (!db) return null;
|
|
3507
3571
|
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 };
|
|
3572
|
+
ensureSchema(db);
|
|
3573
|
+
return fn(db);
|
|
3517
3574
|
} catch {
|
|
3518
3575
|
return null;
|
|
3576
|
+
} finally {
|
|
3577
|
+
db.close();
|
|
3519
3578
|
}
|
|
3520
3579
|
}
|
|
3521
|
-
function
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3580
|
+
function ensureSchema(db) {
|
|
3581
|
+
db.exec(`
|
|
3582
|
+
CREATE TABLE IF NOT EXISTS cache_meta (
|
|
3583
|
+
key TEXT PRIMARY KEY,
|
|
3584
|
+
value TEXT NOT NULL
|
|
3585
|
+
);
|
|
3586
|
+
|
|
3587
|
+
CREATE TABLE IF NOT EXISTS agent_cache (
|
|
3588
|
+
agent_name TEXT PRIMARY KEY,
|
|
3589
|
+
timestamp INTEGER NOT NULL
|
|
3590
|
+
);
|
|
3591
|
+
|
|
3592
|
+
CREATE TABLE IF NOT EXISTS cached_sessions (
|
|
3593
|
+
agent_name TEXT NOT NULL,
|
|
3594
|
+
session_id TEXT NOT NULL,
|
|
3595
|
+
session_json TEXT NOT NULL,
|
|
3596
|
+
meta_json TEXT,
|
|
3597
|
+
PRIMARY KEY (agent_name, session_id)
|
|
3598
|
+
);
|
|
3599
|
+
|
|
3600
|
+
CREATE TABLE IF NOT EXISTS session_documents (
|
|
3601
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
3602
|
+
agent_name TEXT NOT NULL,
|
|
3603
|
+
session_id TEXT NOT NULL,
|
|
3604
|
+
slug TEXT NOT NULL,
|
|
3605
|
+
title TEXT NOT NULL,
|
|
3606
|
+
directory TEXT NOT NULL,
|
|
3607
|
+
time_created INTEGER NOT NULL,
|
|
3608
|
+
time_updated INTEGER,
|
|
3609
|
+
activity_time INTEGER NOT NULL,
|
|
3610
|
+
content_text TEXT NOT NULL,
|
|
3611
|
+
content_hash TEXT NOT NULL,
|
|
3612
|
+
indexed_at INTEGER NOT NULL,
|
|
3613
|
+
UNIQUE(agent_name, session_id)
|
|
3614
|
+
);
|
|
3615
|
+
|
|
3616
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS session_documents_fts USING fts5(
|
|
3617
|
+
title,
|
|
3618
|
+
content_text,
|
|
3619
|
+
content='session_documents',
|
|
3620
|
+
content_rowid='id'
|
|
3621
|
+
);
|
|
3622
|
+
|
|
3623
|
+
CREATE TRIGGER IF NOT EXISTS session_documents_ai AFTER INSERT ON session_documents BEGIN
|
|
3624
|
+
INSERT INTO session_documents_fts(rowid, title, content_text)
|
|
3625
|
+
VALUES (new.id, new.title, new.content_text);
|
|
3626
|
+
END;
|
|
3627
|
+
|
|
3628
|
+
CREATE TRIGGER IF NOT EXISTS session_documents_ad AFTER DELETE ON session_documents BEGIN
|
|
3629
|
+
INSERT INTO session_documents_fts(session_documents_fts, rowid, title, content_text)
|
|
3630
|
+
VALUES ('delete', old.id, old.title, old.content_text);
|
|
3631
|
+
END;
|
|
3632
|
+
|
|
3633
|
+
CREATE TRIGGER IF NOT EXISTS session_documents_au AFTER UPDATE ON session_documents BEGIN
|
|
3634
|
+
INSERT INTO session_documents_fts(session_documents_fts, rowid, title, content_text)
|
|
3635
|
+
VALUES ('delete', old.id, old.title, old.content_text);
|
|
3636
|
+
INSERT INTO session_documents_fts(rowid, title, content_text)
|
|
3637
|
+
VALUES (new.id, new.title, new.content_text);
|
|
3638
|
+
END;
|
|
3639
|
+
`);
|
|
3640
|
+
const versionRow = db.prepare("SELECT value FROM cache_meta WHERE key = 'version'").get();
|
|
3641
|
+
const version = Number(versionRow?.value ?? 0);
|
|
3642
|
+
if (version === CACHE_VERSION) {
|
|
3643
|
+
return;
|
|
3644
|
+
}
|
|
3645
|
+
db.exec(`
|
|
3646
|
+
DELETE FROM agent_cache;
|
|
3647
|
+
DELETE FROM cached_sessions;
|
|
3648
|
+
DELETE FROM session_documents;
|
|
3649
|
+
INSERT INTO session_documents_fts(session_documents_fts) VALUES ('rebuild');
|
|
3650
|
+
INSERT INTO cache_meta(key, value)
|
|
3651
|
+
VALUES ('version', '${CACHE_VERSION}')
|
|
3652
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value;
|
|
3653
|
+
`);
|
|
3654
|
+
}
|
|
3655
|
+
function sessionContentHash(session) {
|
|
3656
|
+
return JSON.stringify([
|
|
3657
|
+
session.slug,
|
|
3658
|
+
session.title,
|
|
3659
|
+
session.directory,
|
|
3660
|
+
session.time_created,
|
|
3661
|
+
session.time_updated ?? session.time_created,
|
|
3662
|
+
session.stats.message_count,
|
|
3663
|
+
session.stats.total_input_tokens,
|
|
3664
|
+
session.stats.total_output_tokens,
|
|
3665
|
+
session.stats.total_cost,
|
|
3666
|
+
session.stats.total_tokens ?? 0
|
|
3667
|
+
]);
|
|
3668
|
+
}
|
|
3669
|
+
function escapeFtsTerm(value) {
|
|
3670
|
+
return value.replaceAll('"', '""');
|
|
3671
|
+
}
|
|
3672
|
+
function toFtsQuery(input) {
|
|
3673
|
+
const tokens = input.match(/"[^"]+"|\S+/g) ?? [];
|
|
3674
|
+
return tokens.map((token) => {
|
|
3675
|
+
if (/^OR$/i.test(token)) {
|
|
3676
|
+
return "OR";
|
|
3537
3677
|
}
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3678
|
+
if (token.startsWith('"') && token.endsWith('"')) {
|
|
3679
|
+
return `"${escapeFtsTerm(token.slice(1, -1))}"`;
|
|
3680
|
+
}
|
|
3681
|
+
return `"${escapeFtsTerm(token)}"`;
|
|
3682
|
+
}).join(" ");
|
|
3683
|
+
}
|
|
3684
|
+
function appendPlainText(value, chunks) {
|
|
3685
|
+
if (value == null) return;
|
|
3686
|
+
if (typeof value === "string") {
|
|
3687
|
+
const normalized = value.trim();
|
|
3688
|
+
if (normalized) {
|
|
3689
|
+
chunks.push(normalized);
|
|
3690
|
+
}
|
|
3691
|
+
return;
|
|
3692
|
+
}
|
|
3693
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
3694
|
+
chunks.push(String(value));
|
|
3695
|
+
return;
|
|
3696
|
+
}
|
|
3697
|
+
if (Array.isArray(value)) {
|
|
3698
|
+
for (const item of value) {
|
|
3699
|
+
appendPlainText(item, chunks);
|
|
3700
|
+
}
|
|
3701
|
+
return;
|
|
3702
|
+
}
|
|
3703
|
+
if (typeof value === "object") {
|
|
3704
|
+
for (const nested of Object.values(value)) {
|
|
3705
|
+
appendPlainText(nested, chunks);
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
function buildSessionContent(session) {
|
|
3710
|
+
const chunks = [];
|
|
3711
|
+
appendPlainText(session.title, chunks);
|
|
3712
|
+
for (const message of session.messages) {
|
|
3713
|
+
chunks.push(message.role);
|
|
3714
|
+
appendPlainText(message.agent, chunks);
|
|
3715
|
+
appendPlainText(message.model, chunks);
|
|
3716
|
+
for (const part of message.parts) {
|
|
3717
|
+
appendPlainText(part.type, chunks);
|
|
3718
|
+
appendPlainText(part.title, chunks);
|
|
3719
|
+
appendPlainText(part.nickname, chunks);
|
|
3720
|
+
appendPlainText(part.tool, chunks);
|
|
3721
|
+
appendPlainText(part.text, chunks);
|
|
3722
|
+
appendPlainText(part.input, chunks);
|
|
3723
|
+
appendPlainText(part.output, chunks);
|
|
3724
|
+
appendPlainText(part.state, chunks);
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
return chunks.join("\n");
|
|
3728
|
+
}
|
|
3729
|
+
function deleteLegacyCacheFile() {
|
|
3730
|
+
const legacyPath = getLegacyCachePath();
|
|
3731
|
+
if (!existsSync7(legacyPath)) {
|
|
3732
|
+
return;
|
|
3733
|
+
}
|
|
3734
|
+
try {
|
|
3735
|
+
unlinkSync(legacyPath);
|
|
3546
3736
|
} catch {
|
|
3547
3737
|
}
|
|
3548
3738
|
}
|
|
3739
|
+
function loadCachedSessions(agentName) {
|
|
3740
|
+
if (!hasCacheStorage()) {
|
|
3741
|
+
return null;
|
|
3742
|
+
}
|
|
3743
|
+
return withCacheDb((db) => {
|
|
3744
|
+
const timestampRow = db.prepare("SELECT timestamp AS value FROM agent_cache WHERE agent_name = ?").get(agentName);
|
|
3745
|
+
const timestamp = Number(timestampRow?.value ?? 0);
|
|
3746
|
+
if (!timestamp || Date.now() - timestamp > CACHE_TTL) {
|
|
3747
|
+
return null;
|
|
3748
|
+
}
|
|
3749
|
+
const rows = db.prepare(
|
|
3750
|
+
`
|
|
3751
|
+
SELECT session_json, meta_json
|
|
3752
|
+
FROM cached_sessions
|
|
3753
|
+
WHERE agent_name = ?
|
|
3754
|
+
ORDER BY rowid
|
|
3755
|
+
`
|
|
3756
|
+
).all(agentName);
|
|
3757
|
+
const sessions = [];
|
|
3758
|
+
const meta = {};
|
|
3759
|
+
for (const row of rows) {
|
|
3760
|
+
if (!row.session_json) {
|
|
3761
|
+
continue;
|
|
3762
|
+
}
|
|
3763
|
+
const session = JSON.parse(row.session_json);
|
|
3764
|
+
sessions.push(session);
|
|
3765
|
+
if (row.meta_json) {
|
|
3766
|
+
meta[session.id] = JSON.parse(row.meta_json);
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
return { sessions, meta, timestamp };
|
|
3770
|
+
});
|
|
3771
|
+
}
|
|
3772
|
+
function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
3773
|
+
withCacheDb((db) => {
|
|
3774
|
+
const deleteAgent = db.prepare("DELETE FROM agent_cache WHERE agent_name = ?");
|
|
3775
|
+
const deleteSessions = db.prepare("DELETE FROM cached_sessions WHERE agent_name = ?");
|
|
3776
|
+
const upsertAgent = db.prepare(`
|
|
3777
|
+
INSERT INTO agent_cache(agent_name, timestamp)
|
|
3778
|
+
VALUES (?, ?)
|
|
3779
|
+
ON CONFLICT(agent_name) DO UPDATE SET timestamp = excluded.timestamp
|
|
3780
|
+
`);
|
|
3781
|
+
const insertSession = db.prepare(`
|
|
3782
|
+
INSERT INTO cached_sessions(agent_name, session_id, session_json, meta_json)
|
|
3783
|
+
VALUES (?, ?, ?, ?)
|
|
3784
|
+
`);
|
|
3785
|
+
const write = db.transaction(() => {
|
|
3786
|
+
const timestamp = Date.now();
|
|
3787
|
+
deleteAgent.run(agentName);
|
|
3788
|
+
deleteSessions.run(agentName);
|
|
3789
|
+
upsertAgent.run(agentName, timestamp);
|
|
3790
|
+
for (const session of sessions) {
|
|
3791
|
+
insertSession.run(
|
|
3792
|
+
agentName,
|
|
3793
|
+
session.id,
|
|
3794
|
+
JSON.stringify(session),
|
|
3795
|
+
meta[session.id] ? JSON.stringify(meta[session.id]) : null
|
|
3796
|
+
);
|
|
3797
|
+
}
|
|
3798
|
+
});
|
|
3799
|
+
write();
|
|
3800
|
+
deleteLegacyCacheFile();
|
|
3801
|
+
});
|
|
3802
|
+
}
|
|
3549
3803
|
function clearCache() {
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3804
|
+
if (!hasCacheStorage()) {
|
|
3805
|
+
deleteLegacyCacheFile();
|
|
3806
|
+
return;
|
|
3807
|
+
}
|
|
3808
|
+
withCacheDb((db) => {
|
|
3809
|
+
db.exec(`
|
|
3810
|
+
DELETE FROM agent_cache;
|
|
3811
|
+
DELETE FROM cached_sessions;
|
|
3812
|
+
`);
|
|
3813
|
+
});
|
|
3814
|
+
deleteLegacyCacheFile();
|
|
3815
|
+
const cachePath = getCachePath();
|
|
3816
|
+
const walPath = `${cachePath}-wal`;
|
|
3817
|
+
const shmPath = `${cachePath}-shm`;
|
|
3818
|
+
for (const filePath of [walPath, shmPath]) {
|
|
3819
|
+
if (!existsSync7(filePath)) {
|
|
3820
|
+
continue;
|
|
3821
|
+
}
|
|
3822
|
+
try {
|
|
3823
|
+
rmSync(filePath, { force: true });
|
|
3824
|
+
} catch {
|
|
3559
3825
|
}
|
|
3560
|
-
} catch {
|
|
3561
3826
|
}
|
|
3562
3827
|
}
|
|
3563
3828
|
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 {
|
|
3829
|
+
if (!hasCacheStorage()) {
|
|
3576
3830
|
return { lastScanTime: null, size: 0 };
|
|
3577
3831
|
}
|
|
3832
|
+
const info = withCacheDb((db) => {
|
|
3833
|
+
const timestampRow = db.prepare("SELECT MAX(timestamp) AS value FROM agent_cache").get();
|
|
3834
|
+
const sizeRow = db.prepare("SELECT COUNT(*) AS value FROM cached_sessions").get();
|
|
3835
|
+
const lastScanTime = Number(timestampRow?.value ?? 0) || null;
|
|
3836
|
+
const size = Number(sizeRow?.value ?? 0);
|
|
3837
|
+
return { lastScanTime, size };
|
|
3838
|
+
});
|
|
3839
|
+
return info ?? { lastScanTime: null, size: 0 };
|
|
3840
|
+
}
|
|
3841
|
+
function syncSessionSearchIndex(agentName, sessions, loadSessionData) {
|
|
3842
|
+
if (!hasCacheStorage()) {
|
|
3843
|
+
return;
|
|
3844
|
+
}
|
|
3845
|
+
withCacheDb((db) => {
|
|
3846
|
+
const existingRows = db.prepare(
|
|
3847
|
+
"SELECT session_id, content_hash FROM session_documents WHERE agent_name = ? ORDER BY id"
|
|
3848
|
+
).all(agentName);
|
|
3849
|
+
const existingMap = new Map(
|
|
3850
|
+
existingRows.map((row) => [String(row.session_id), String(row.content_hash ?? "")])
|
|
3851
|
+
);
|
|
3852
|
+
const sessionMap = new Map(sessions.map((session) => [session.id, session]));
|
|
3853
|
+
const toDelete = existingRows.map((row) => String(row.session_id)).filter((sessionId) => !sessionMap.has(sessionId));
|
|
3854
|
+
const toUpsert = sessions.filter(
|
|
3855
|
+
(session) => existingMap.get(session.id) !== sessionContentHash(session)
|
|
3856
|
+
);
|
|
3857
|
+
const loaded = toUpsert.map((session) => {
|
|
3858
|
+
try {
|
|
3859
|
+
const data = loadSessionData(session.id);
|
|
3860
|
+
return {
|
|
3861
|
+
session,
|
|
3862
|
+
contentText: buildSessionContent(data),
|
|
3863
|
+
contentHash: sessionContentHash(session)
|
|
3864
|
+
};
|
|
3865
|
+
} catch {
|
|
3866
|
+
return null;
|
|
3867
|
+
}
|
|
3868
|
+
}).filter((entry) => entry !== null);
|
|
3869
|
+
const deleteRow = db.prepare(
|
|
3870
|
+
"DELETE FROM session_documents WHERE agent_name = ? AND session_id = ?"
|
|
3871
|
+
);
|
|
3872
|
+
const upsertRow = db.prepare(`
|
|
3873
|
+
INSERT INTO session_documents(
|
|
3874
|
+
agent_name,
|
|
3875
|
+
session_id,
|
|
3876
|
+
slug,
|
|
3877
|
+
title,
|
|
3878
|
+
directory,
|
|
3879
|
+
time_created,
|
|
3880
|
+
time_updated,
|
|
3881
|
+
activity_time,
|
|
3882
|
+
content_text,
|
|
3883
|
+
content_hash,
|
|
3884
|
+
indexed_at
|
|
3885
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3886
|
+
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
3887
|
+
slug = excluded.slug,
|
|
3888
|
+
title = excluded.title,
|
|
3889
|
+
directory = excluded.directory,
|
|
3890
|
+
time_created = excluded.time_created,
|
|
3891
|
+
time_updated = excluded.time_updated,
|
|
3892
|
+
activity_time = excluded.activity_time,
|
|
3893
|
+
content_text = excluded.content_text,
|
|
3894
|
+
content_hash = excluded.content_hash,
|
|
3895
|
+
indexed_at = excluded.indexed_at
|
|
3896
|
+
`);
|
|
3897
|
+
const write = db.transaction(() => {
|
|
3898
|
+
for (const sessionId of toDelete) {
|
|
3899
|
+
deleteRow.run(agentName, sessionId);
|
|
3900
|
+
}
|
|
3901
|
+
for (const entry of loaded) {
|
|
3902
|
+
const activityTime = entry.session.time_updated ?? entry.session.time_created;
|
|
3903
|
+
upsertRow.run(
|
|
3904
|
+
agentName,
|
|
3905
|
+
entry.session.id,
|
|
3906
|
+
entry.session.slug,
|
|
3907
|
+
entry.session.title,
|
|
3908
|
+
entry.session.directory,
|
|
3909
|
+
entry.session.time_created,
|
|
3910
|
+
entry.session.time_updated ?? null,
|
|
3911
|
+
activityTime,
|
|
3912
|
+
entry.contentText,
|
|
3913
|
+
entry.contentHash,
|
|
3914
|
+
Date.now()
|
|
3915
|
+
);
|
|
3916
|
+
}
|
|
3917
|
+
});
|
|
3918
|
+
write();
|
|
3919
|
+
});
|
|
3920
|
+
}
|
|
3921
|
+
function searchSessions(query, options = {}) {
|
|
3922
|
+
const normalizedQuery = query.trim();
|
|
3923
|
+
if (!normalizedQuery || !hasCacheStorage()) {
|
|
3924
|
+
return [];
|
|
3925
|
+
}
|
|
3926
|
+
const ftsQuery = toFtsQuery(normalizedQuery);
|
|
3927
|
+
const results = withCacheDb((db) => {
|
|
3928
|
+
const rows = db.prepare(
|
|
3929
|
+
`
|
|
3930
|
+
SELECT
|
|
3931
|
+
d.agent_name,
|
|
3932
|
+
d.session_id,
|
|
3933
|
+
d.slug,
|
|
3934
|
+
d.title,
|
|
3935
|
+
d.directory,
|
|
3936
|
+
d.time_created,
|
|
3937
|
+
d.time_updated,
|
|
3938
|
+
COALESCE(
|
|
3939
|
+
NULLIF(snippet(session_documents_fts, 1, '<mark>', '</mark>', ' \u2026 ', 18), ''),
|
|
3940
|
+
highlight(session_documents_fts, 0, '<mark>', '</mark>')
|
|
3941
|
+
) AS snippet
|
|
3942
|
+
FROM session_documents_fts
|
|
3943
|
+
JOIN session_documents d ON d.id = session_documents_fts.rowid
|
|
3944
|
+
WHERE session_documents_fts MATCH ?
|
|
3945
|
+
AND (? IS NULL OR d.agent_name = ?)
|
|
3946
|
+
AND (? IS NULL OR LOWER(d.directory) LIKE ?)
|
|
3947
|
+
AND (? IS NULL OR d.activity_time >= ?)
|
|
3948
|
+
AND (? IS NULL OR d.activity_time <= ?)
|
|
3949
|
+
ORDER BY bm25(session_documents_fts, 8.0, 1.0), d.activity_time DESC
|
|
3950
|
+
LIMIT ?
|
|
3951
|
+
`
|
|
3952
|
+
).all(
|
|
3953
|
+
ftsQuery,
|
|
3954
|
+
options.agent ?? null,
|
|
3955
|
+
options.agent ?? null,
|
|
3956
|
+
options.cwd?.toLowerCase() ?? null,
|
|
3957
|
+
options.cwd ? `%${options.cwd.toLowerCase()}%` : null,
|
|
3958
|
+
options.from ?? null,
|
|
3959
|
+
options.from ?? null,
|
|
3960
|
+
options.to ?? null,
|
|
3961
|
+
options.to ?? null,
|
|
3962
|
+
options.limit ?? 50
|
|
3963
|
+
);
|
|
3964
|
+
return rows.map((row) => ({
|
|
3965
|
+
agentName: String(row.agent_name),
|
|
3966
|
+
session: {
|
|
3967
|
+
id: String(row.session_id),
|
|
3968
|
+
slug: String(row.slug),
|
|
3969
|
+
title: String(row.title),
|
|
3970
|
+
directory: String(row.directory),
|
|
3971
|
+
time_created: Number(row.time_created),
|
|
3972
|
+
time_updated: row.time_updated == null ? void 0 : Number(row.time_updated),
|
|
3973
|
+
stats: {
|
|
3974
|
+
message_count: 0,
|
|
3975
|
+
total_input_tokens: 0,
|
|
3976
|
+
total_output_tokens: 0,
|
|
3977
|
+
total_cost: 0
|
|
3978
|
+
}
|
|
3979
|
+
},
|
|
3980
|
+
snippet: String(row.snippet ?? "")
|
|
3981
|
+
}));
|
|
3982
|
+
});
|
|
3983
|
+
return results ?? [];
|
|
3578
3984
|
}
|
|
3579
3985
|
function isPathScopeMatch(queryPath, sessionPath) {
|
|
3580
3986
|
if (!sessionPath) return false;
|
|
@@ -3710,6 +4116,221 @@ async function scanSessions(options = {}, onProgress) {
|
|
|
3710
4116
|
async function scanSessionsAsync(options = {}, onProgress) {
|
|
3711
4117
|
return scanSessions(options, onProgress);
|
|
3712
4118
|
}
|
|
4119
|
+
var BOOKMARK_DB_FILENAME = "state.db";
|
|
4120
|
+
var BOOKMARK_DB_VERSION = 1;
|
|
4121
|
+
function getStateDir() {
|
|
4122
|
+
const p = platform2();
|
|
4123
|
+
if (p === "darwin") {
|
|
4124
|
+
return join8(homedir3(), "Library", "Application Support", "codesesh");
|
|
4125
|
+
}
|
|
4126
|
+
if (p === "win32") {
|
|
4127
|
+
const appData = process.env.APPDATA ?? process.env.LOCALAPPDATA;
|
|
4128
|
+
return join8(appData ?? join8(homedir3(), "AppData", "Roaming"), "codesesh");
|
|
4129
|
+
}
|
|
4130
|
+
return join8(process.env.XDG_DATA_HOME ?? join8(homedir3(), ".local", "share"), "codesesh");
|
|
4131
|
+
}
|
|
4132
|
+
function getStateDbPath() {
|
|
4133
|
+
return join8(getStateDir(), BOOKMARK_DB_FILENAME);
|
|
4134
|
+
}
|
|
4135
|
+
function ensureSchema2(db) {
|
|
4136
|
+
db.exec(`
|
|
4137
|
+
CREATE TABLE IF NOT EXISTS state_meta (
|
|
4138
|
+
key TEXT PRIMARY KEY,
|
|
4139
|
+
value TEXT NOT NULL
|
|
4140
|
+
);
|
|
4141
|
+
|
|
4142
|
+
CREATE TABLE IF NOT EXISTS bookmarks (
|
|
4143
|
+
agent_name TEXT NOT NULL,
|
|
4144
|
+
session_id TEXT NOT NULL,
|
|
4145
|
+
slug TEXT NOT NULL,
|
|
4146
|
+
title TEXT NOT NULL,
|
|
4147
|
+
directory TEXT NOT NULL,
|
|
4148
|
+
time_created INTEGER NOT NULL,
|
|
4149
|
+
time_updated INTEGER,
|
|
4150
|
+
stats_json TEXT NOT NULL,
|
|
4151
|
+
bookmarked_at INTEGER NOT NULL,
|
|
4152
|
+
PRIMARY KEY (agent_name, session_id)
|
|
4153
|
+
);
|
|
4154
|
+
`);
|
|
4155
|
+
const row = db.prepare("SELECT value FROM state_meta WHERE key = 'version'").get();
|
|
4156
|
+
const version = Number(row?.value ?? 0);
|
|
4157
|
+
if (version === BOOKMARK_DB_VERSION) return;
|
|
4158
|
+
db.prepare(
|
|
4159
|
+
`
|
|
4160
|
+
INSERT INTO state_meta(key, value)
|
|
4161
|
+
VALUES ('version', ?)
|
|
4162
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
4163
|
+
`
|
|
4164
|
+
).run(String(BOOKMARK_DB_VERSION));
|
|
4165
|
+
}
|
|
4166
|
+
function withStateDb(fn) {
|
|
4167
|
+
const db = openDb(getStateDbPath());
|
|
4168
|
+
if (!db) {
|
|
4169
|
+
throw new Error("SQLite state database is unavailable");
|
|
4170
|
+
}
|
|
4171
|
+
try {
|
|
4172
|
+
ensureSchema2(db);
|
|
4173
|
+
return fn(db);
|
|
4174
|
+
} finally {
|
|
4175
|
+
db.close();
|
|
4176
|
+
}
|
|
4177
|
+
}
|
|
4178
|
+
function toBookmarkRecord(row) {
|
|
4179
|
+
return {
|
|
4180
|
+
agentKey: String(row.agent_name ?? ""),
|
|
4181
|
+
sessionId: String(row.session_id ?? ""),
|
|
4182
|
+
fullPath: String(row.slug ?? ""),
|
|
4183
|
+
title: String(row.title ?? ""),
|
|
4184
|
+
directory: String(row.directory ?? ""),
|
|
4185
|
+
time_created: Number(row.time_created ?? 0),
|
|
4186
|
+
time_updated: row.time_updated == null ? void 0 : Number(row.time_updated),
|
|
4187
|
+
stats: JSON.parse(String(row.stats_json ?? "{}")),
|
|
4188
|
+
bookmarked_at: Number(row.bookmarked_at ?? 0)
|
|
4189
|
+
};
|
|
4190
|
+
}
|
|
4191
|
+
function listBookmarks() {
|
|
4192
|
+
return withStateDb((db) => {
|
|
4193
|
+
const rows = db.prepare(
|
|
4194
|
+
`
|
|
4195
|
+
SELECT
|
|
4196
|
+
agent_name,
|
|
4197
|
+
session_id,
|
|
4198
|
+
slug,
|
|
4199
|
+
title,
|
|
4200
|
+
directory,
|
|
4201
|
+
time_created,
|
|
4202
|
+
time_updated,
|
|
4203
|
+
stats_json,
|
|
4204
|
+
bookmarked_at
|
|
4205
|
+
FROM bookmarks
|
|
4206
|
+
ORDER BY COALESCE(time_updated, time_created) DESC, bookmarked_at DESC
|
|
4207
|
+
`
|
|
4208
|
+
).all();
|
|
4209
|
+
return rows.map(toBookmarkRecord);
|
|
4210
|
+
});
|
|
4211
|
+
}
|
|
4212
|
+
function upsertBookmark(bookmark) {
|
|
4213
|
+
return withStateDb((db) => {
|
|
4214
|
+
const existing = db.prepare(
|
|
4215
|
+
`
|
|
4216
|
+
SELECT bookmarked_at
|
|
4217
|
+
FROM bookmarks
|
|
4218
|
+
WHERE agent_name = ? AND session_id = ?
|
|
4219
|
+
`
|
|
4220
|
+
).get(bookmark.agentKey, bookmark.sessionId);
|
|
4221
|
+
const bookmarkedAt = Number(existing?.bookmarked_at ?? Date.now());
|
|
4222
|
+
db.prepare(
|
|
4223
|
+
`
|
|
4224
|
+
INSERT INTO bookmarks(
|
|
4225
|
+
agent_name,
|
|
4226
|
+
session_id,
|
|
4227
|
+
slug,
|
|
4228
|
+
title,
|
|
4229
|
+
directory,
|
|
4230
|
+
time_created,
|
|
4231
|
+
time_updated,
|
|
4232
|
+
stats_json,
|
|
4233
|
+
bookmarked_at
|
|
4234
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4235
|
+
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
4236
|
+
slug = excluded.slug,
|
|
4237
|
+
title = excluded.title,
|
|
4238
|
+
directory = excluded.directory,
|
|
4239
|
+
time_created = excluded.time_created,
|
|
4240
|
+
time_updated = excluded.time_updated,
|
|
4241
|
+
stats_json = excluded.stats_json
|
|
4242
|
+
`
|
|
4243
|
+
).run(
|
|
4244
|
+
bookmark.agentKey,
|
|
4245
|
+
bookmark.sessionId,
|
|
4246
|
+
bookmark.fullPath,
|
|
4247
|
+
bookmark.title,
|
|
4248
|
+
bookmark.directory,
|
|
4249
|
+
bookmark.time_created,
|
|
4250
|
+
bookmark.time_updated ?? null,
|
|
4251
|
+
JSON.stringify(bookmark.stats),
|
|
4252
|
+
bookmarkedAt
|
|
4253
|
+
);
|
|
4254
|
+
return { ...bookmark, bookmarked_at: bookmarkedAt };
|
|
4255
|
+
});
|
|
4256
|
+
}
|
|
4257
|
+
function importBookmarks(bookmarks) {
|
|
4258
|
+
return withStateDb((db) => {
|
|
4259
|
+
const existingRows = db.prepare("SELECT agent_name, session_id, bookmarked_at FROM bookmarks").all();
|
|
4260
|
+
const existingTimes = new Map(
|
|
4261
|
+
existingRows.map((row) => [
|
|
4262
|
+
`${String(row.agent_name ?? "")}:${String(row.session_id ?? "")}`,
|
|
4263
|
+
Number(row.bookmarked_at ?? 0)
|
|
4264
|
+
])
|
|
4265
|
+
);
|
|
4266
|
+
const upsert = db.prepare(
|
|
4267
|
+
`
|
|
4268
|
+
INSERT INTO bookmarks(
|
|
4269
|
+
agent_name,
|
|
4270
|
+
session_id,
|
|
4271
|
+
slug,
|
|
4272
|
+
title,
|
|
4273
|
+
directory,
|
|
4274
|
+
time_created,
|
|
4275
|
+
time_updated,
|
|
4276
|
+
stats_json,
|
|
4277
|
+
bookmarked_at
|
|
4278
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4279
|
+
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
4280
|
+
slug = excluded.slug,
|
|
4281
|
+
title = excluded.title,
|
|
4282
|
+
directory = excluded.directory,
|
|
4283
|
+
time_created = excluded.time_created,
|
|
4284
|
+
time_updated = excluded.time_updated,
|
|
4285
|
+
stats_json = excluded.stats_json
|
|
4286
|
+
`
|
|
4287
|
+
);
|
|
4288
|
+
const write = db.transaction(() => {
|
|
4289
|
+
for (const bookmark of bookmarks) {
|
|
4290
|
+
const key = `${bookmark.agentKey}:${bookmark.sessionId}`;
|
|
4291
|
+
upsert.run(
|
|
4292
|
+
bookmark.agentKey,
|
|
4293
|
+
bookmark.sessionId,
|
|
4294
|
+
bookmark.fullPath,
|
|
4295
|
+
bookmark.title,
|
|
4296
|
+
bookmark.directory,
|
|
4297
|
+
bookmark.time_created,
|
|
4298
|
+
bookmark.time_updated ?? null,
|
|
4299
|
+
JSON.stringify(bookmark.stats),
|
|
4300
|
+
existingTimes.get(key) ?? Date.now()
|
|
4301
|
+
);
|
|
4302
|
+
}
|
|
4303
|
+
});
|
|
4304
|
+
write();
|
|
4305
|
+
const rows = db.prepare(
|
|
4306
|
+
`
|
|
4307
|
+
SELECT
|
|
4308
|
+
agent_name,
|
|
4309
|
+
session_id,
|
|
4310
|
+
slug,
|
|
4311
|
+
title,
|
|
4312
|
+
directory,
|
|
4313
|
+
time_created,
|
|
4314
|
+
time_updated,
|
|
4315
|
+
stats_json,
|
|
4316
|
+
bookmarked_at
|
|
4317
|
+
FROM bookmarks
|
|
4318
|
+
ORDER BY COALESCE(time_updated, time_created) DESC, bookmarked_at DESC
|
|
4319
|
+
`
|
|
4320
|
+
).all();
|
|
4321
|
+
return rows.map(toBookmarkRecord);
|
|
4322
|
+
});
|
|
4323
|
+
}
|
|
4324
|
+
function deleteBookmark(agentKey, sessionId) {
|
|
4325
|
+
withStateDb((db) => {
|
|
4326
|
+
db.prepare(
|
|
4327
|
+
`
|
|
4328
|
+
DELETE FROM bookmarks
|
|
4329
|
+
WHERE agent_name = ? AND session_id = ?
|
|
4330
|
+
`
|
|
4331
|
+
).run(agentKey, sessionId);
|
|
4332
|
+
});
|
|
4333
|
+
}
|
|
3713
4334
|
|
|
3714
4335
|
export {
|
|
3715
4336
|
registerAgent,
|
|
@@ -3728,13 +4349,20 @@ export {
|
|
|
3728
4349
|
resolveSessionTitle,
|
|
3729
4350
|
perf,
|
|
3730
4351
|
openDbReadOnly,
|
|
4352
|
+
openDb,
|
|
3731
4353
|
isSqliteAvailable,
|
|
3732
4354
|
loadCachedSessions,
|
|
3733
4355
|
saveCachedSessions,
|
|
3734
4356
|
clearCache,
|
|
3735
4357
|
getCacheInfo,
|
|
4358
|
+
syncSessionSearchIndex,
|
|
4359
|
+
searchSessions,
|
|
3736
4360
|
filterSessions,
|
|
3737
4361
|
scanSessions,
|
|
3738
|
-
scanSessionsAsync
|
|
4362
|
+
scanSessionsAsync,
|
|
4363
|
+
listBookmarks,
|
|
4364
|
+
upsertBookmark,
|
|
4365
|
+
importBookmarks,
|
|
4366
|
+
deleteBookmark
|
|
3739
4367
|
};
|
|
3740
|
-
//# sourceMappingURL=chunk-
|
|
4368
|
+
//# sourceMappingURL=chunk-EIIG7J6V.js.map
|