claude-transcript-viewer 1.1.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 +215 -0
- package/dist/api/search.d.ts +50 -0
- package/dist/api/search.js +181 -0
- package/dist/api/search.js.map +1 -0
- package/dist/api/snippets.d.ts +2 -0
- package/dist/api/snippets.js +49 -0
- package/dist/api/snippets.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +52 -0
- package/dist/config.js.map +1 -0
- package/dist/db/chunks.d.ts +14 -0
- package/dist/db/chunks.js +43 -0
- package/dist/db/chunks.js.map +1 -0
- package/dist/db/conversations.d.ts +16 -0
- package/dist/db/conversations.js +40 -0
- package/dist/db/conversations.js.map +1 -0
- package/dist/db/index.d.ts +6 -0
- package/dist/db/index.js +40 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.d.ts +5 -0
- package/dist/db/schema.js +71 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/embeddings/client.d.ts +16 -0
- package/dist/embeddings/client.js +155 -0
- package/dist/embeddings/client.js.map +1 -0
- package/dist/indexer/changeDetection.d.ts +16 -0
- package/dist/indexer/changeDetection.js +81 -0
- package/dist/indexer/changeDetection.js.map +1 -0
- package/dist/indexer/chunker.d.ts +5 -0
- package/dist/indexer/chunker.js +44 -0
- package/dist/indexer/chunker.js.map +1 -0
- package/dist/indexer/fileUtils.d.ts +2 -0
- package/dist/indexer/fileUtils.js +9 -0
- package/dist/indexer/fileUtils.js.map +1 -0
- package/dist/indexer/index.d.ts +19 -0
- package/dist/indexer/index.js +267 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/indexer/parser.d.ts +12 -0
- package/dist/indexer/parser.js +45 -0
- package/dist/indexer/parser.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +1851 -0
- package/dist/server.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Indexer for JSONL transcripts.
|
|
3
|
+
* Parses transcripts, chunks content, gets embeddings, stores in database.
|
|
4
|
+
*/
|
|
5
|
+
import { readdirSync } from "fs";
|
|
6
|
+
import { join, basename } from "path";
|
|
7
|
+
import { createDatabase, getDatabase, closeDatabase, setMetadata } from "../db/index.js";
|
|
8
|
+
import { insertConversation, getConversation, deleteConversation } from "../db/conversations.js";
|
|
9
|
+
import { insertChunk } from "../db/chunks.js";
|
|
10
|
+
import { parseTranscript } from "./parser.js";
|
|
11
|
+
import { chunkText } from "./chunker.js";
|
|
12
|
+
import { getFileHash, getFileMtime } from "./fileUtils.js";
|
|
13
|
+
import { createEmbeddingClient } from "../embeddings/client.js";
|
|
14
|
+
/**
|
|
15
|
+
* Find all JSONL files in a directory recursively.
|
|
16
|
+
*/
|
|
17
|
+
function findJsonlFiles(dir) {
|
|
18
|
+
const files = [];
|
|
19
|
+
function walk(currentDir) {
|
|
20
|
+
try {
|
|
21
|
+
const entries = readdirSync(currentDir, { withFileTypes: true });
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
const fullPath = join(currentDir, entry.name);
|
|
24
|
+
if (entry.isDirectory()) {
|
|
25
|
+
walk(fullPath);
|
|
26
|
+
}
|
|
27
|
+
else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
28
|
+
files.push(fullPath);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
// Skip directories we can't read
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
walk(dir);
|
|
37
|
+
return files;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Extract conversation ID from file path.
|
|
41
|
+
* Assumes format: .../project/conversation-id.jsonl
|
|
42
|
+
*/
|
|
43
|
+
function extractConversationId(filePath) {
|
|
44
|
+
return basename(filePath, ".jsonl");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Extract project name from file path.
|
|
48
|
+
* Assumes format: .../project/conversation-id.jsonl
|
|
49
|
+
*/
|
|
50
|
+
function extractProject(filePath, sourceDir) {
|
|
51
|
+
const relativePath = filePath.replace(sourceDir, "").replace(/^\//, "");
|
|
52
|
+
const parts = relativePath.split("/");
|
|
53
|
+
return parts.length > 1 ? parts[0] : "default";
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Index a single transcript file.
|
|
57
|
+
*/
|
|
58
|
+
async function indexTranscript(filePath, sourceDir, embeddingClient, verbose) {
|
|
59
|
+
const db = getDatabase();
|
|
60
|
+
const conversationId = extractConversationId(filePath);
|
|
61
|
+
const project = extractProject(filePath, sourceDir);
|
|
62
|
+
try {
|
|
63
|
+
// Parse the transcript
|
|
64
|
+
const messages = parseTranscript(filePath);
|
|
65
|
+
if (messages.length === 0) {
|
|
66
|
+
return { chunks: 0 };
|
|
67
|
+
}
|
|
68
|
+
// Get file metadata
|
|
69
|
+
const contentHash = getFileHash(filePath);
|
|
70
|
+
const mtime = getFileMtime(filePath);
|
|
71
|
+
// Extract title from first user message
|
|
72
|
+
const firstUserMsg = messages.find((m) => m.role === "user");
|
|
73
|
+
const title = firstUserMsg?.content.slice(0, 100) || "Untitled";
|
|
74
|
+
// Use current time as created_at (JSONL doesn't have timestamps)
|
|
75
|
+
const createdAt = new Date().toISOString();
|
|
76
|
+
// Delete existing conversation if it exists (will cascade delete chunks)
|
|
77
|
+
const existing = getConversation(conversationId);
|
|
78
|
+
if (existing) {
|
|
79
|
+
deleteConversation(conversationId);
|
|
80
|
+
}
|
|
81
|
+
// Insert conversation
|
|
82
|
+
insertConversation({
|
|
83
|
+
id: conversationId,
|
|
84
|
+
project,
|
|
85
|
+
title,
|
|
86
|
+
created_at: createdAt,
|
|
87
|
+
file_path: filePath,
|
|
88
|
+
content_hash: contentHash,
|
|
89
|
+
source_mtime: mtime,
|
|
90
|
+
});
|
|
91
|
+
// Chunk and index each message
|
|
92
|
+
let chunkCount = 0;
|
|
93
|
+
let pageNumber = 1;
|
|
94
|
+
for (const message of messages) {
|
|
95
|
+
const chunks = chunkText(message.content, {
|
|
96
|
+
maxTokens: 300,
|
|
97
|
+
overlap: 50,
|
|
98
|
+
});
|
|
99
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
100
|
+
const chunkText_ = chunks[i];
|
|
101
|
+
let embedding = null;
|
|
102
|
+
// Get embedding if client available
|
|
103
|
+
if (embeddingClient) {
|
|
104
|
+
const result = await embeddingClient.embed(chunkText_);
|
|
105
|
+
if (result) {
|
|
106
|
+
embedding = Buffer.from(new Float32Array(result.embedding).buffer);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
insertChunk({
|
|
110
|
+
conversation_id: conversationId,
|
|
111
|
+
chunk_index: chunkCount,
|
|
112
|
+
page_number: pageNumber,
|
|
113
|
+
role: message.role,
|
|
114
|
+
content: chunkText_,
|
|
115
|
+
embedding,
|
|
116
|
+
});
|
|
117
|
+
chunkCount++;
|
|
118
|
+
}
|
|
119
|
+
// Increment page number periodically (roughly every 10 messages)
|
|
120
|
+
if (chunkCount > 0 && chunkCount % 30 === 0) {
|
|
121
|
+
pageNumber++;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (verbose) {
|
|
125
|
+
console.log(` Indexed ${conversationId}: ${chunkCount} chunks`);
|
|
126
|
+
}
|
|
127
|
+
return { chunks: chunkCount };
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
131
|
+
return { chunks: 0, error };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Run the full indexing process.
|
|
136
|
+
*/
|
|
137
|
+
export async function runIndexer(options) {
|
|
138
|
+
const { sourceDir, databasePath, embedSocketPath, embedUrl, batchSize = 10, verbose = false, } = options;
|
|
139
|
+
const stats = {
|
|
140
|
+
added: 0,
|
|
141
|
+
modified: 0,
|
|
142
|
+
deleted: 0,
|
|
143
|
+
chunks: 0,
|
|
144
|
+
errors: [],
|
|
145
|
+
};
|
|
146
|
+
// Initialize database
|
|
147
|
+
createDatabase(databasePath);
|
|
148
|
+
const db = getDatabase();
|
|
149
|
+
// Connect to embedding server if available (prefer URL over socket path)
|
|
150
|
+
let embeddingClient;
|
|
151
|
+
const embedEndpoint = embedUrl || embedSocketPath;
|
|
152
|
+
if (embedEndpoint) {
|
|
153
|
+
embeddingClient = createEmbeddingClient(embedEndpoint);
|
|
154
|
+
const healthy = await embeddingClient.isHealthy();
|
|
155
|
+
if (healthy) {
|
|
156
|
+
const info = await embeddingClient.getModelInfo();
|
|
157
|
+
console.log(`Connected to embedding server at ${embedEndpoint} (model: ${info?.model}, dim: ${info?.dim})`);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
console.log(`Embedding server not available at ${embedEndpoint} - indexing without embeddings`);
|
|
161
|
+
embeddingClient = undefined;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Find all JSONL files
|
|
165
|
+
const files = findJsonlFiles(sourceDir);
|
|
166
|
+
console.log(`Found ${files.length} JSONL files in ${sourceDir}`);
|
|
167
|
+
// Detect changes
|
|
168
|
+
const indexed = db
|
|
169
|
+
.prepare("SELECT id, file_path, content_hash, source_mtime FROM conversations")
|
|
170
|
+
.all();
|
|
171
|
+
const indexedMap = new Map(indexed.map((c) => [c.file_path, c]));
|
|
172
|
+
const fileSet = new Set(files);
|
|
173
|
+
// Determine what needs to be indexed
|
|
174
|
+
const toIndex = [];
|
|
175
|
+
const toDelete = [];
|
|
176
|
+
for (const file of files) {
|
|
177
|
+
const existing = indexedMap.get(file);
|
|
178
|
+
if (!existing) {
|
|
179
|
+
toIndex.push(file);
|
|
180
|
+
stats.added++;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
// Check if modified
|
|
184
|
+
const mtime = getFileMtime(file);
|
|
185
|
+
if (mtime !== existing.source_mtime) {
|
|
186
|
+
const hash = getFileHash(file);
|
|
187
|
+
if (hash !== existing.content_hash) {
|
|
188
|
+
toIndex.push(file);
|
|
189
|
+
stats.modified++;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Find deleted files
|
|
195
|
+
for (const [filePath, conv] of indexedMap) {
|
|
196
|
+
if (!fileSet.has(filePath)) {
|
|
197
|
+
toDelete.push(conv.id);
|
|
198
|
+
stats.deleted++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Delete removed conversations
|
|
202
|
+
for (const id of toDelete) {
|
|
203
|
+
deleteConversation(id);
|
|
204
|
+
if (verbose) {
|
|
205
|
+
console.log(` Deleted ${id}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Index new/modified files
|
|
209
|
+
console.log(`Indexing ${toIndex.length} files (${stats.added} new, ${stats.modified} modified)`);
|
|
210
|
+
for (let i = 0; i < toIndex.length; i++) {
|
|
211
|
+
const file = toIndex[i];
|
|
212
|
+
const result = await indexTranscript(file, sourceDir, embeddingClient, verbose);
|
|
213
|
+
if (result.error) {
|
|
214
|
+
stats.errors.push(`${file}: ${result.error}`);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
stats.chunks += result.chunks;
|
|
218
|
+
}
|
|
219
|
+
// Progress indicator
|
|
220
|
+
if ((i + 1) % 10 === 0 || i === toIndex.length - 1) {
|
|
221
|
+
console.log(` Progress: ${i + 1}/${toIndex.length}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Update metadata
|
|
225
|
+
setMetadata("last_index_time", new Date().toISOString());
|
|
226
|
+
setMetadata("total_conversations", String(files.length));
|
|
227
|
+
console.log(`\nIndexing complete:`);
|
|
228
|
+
console.log(` Added: ${stats.added}`);
|
|
229
|
+
console.log(` Modified: ${stats.modified}`);
|
|
230
|
+
console.log(` Deleted: ${stats.deleted}`);
|
|
231
|
+
console.log(` Total chunks: ${stats.chunks}`);
|
|
232
|
+
if (stats.errors.length > 0) {
|
|
233
|
+
console.log(` Errors: ${stats.errors.length}`);
|
|
234
|
+
}
|
|
235
|
+
return stats;
|
|
236
|
+
}
|
|
237
|
+
// CLI entry point
|
|
238
|
+
if (process.argv[1]?.endsWith("index.ts") || process.argv[1]?.endsWith("index.js")) {
|
|
239
|
+
const sourceDir = process.argv[2];
|
|
240
|
+
const databasePath = process.argv[3] || "./search.db";
|
|
241
|
+
const embedSocketPath = process.env.EMBED_SOCKET;
|
|
242
|
+
if (!sourceDir) {
|
|
243
|
+
console.error("Usage: npx tsx src/indexer/index.ts <source-dir> [database-path]");
|
|
244
|
+
console.error(" source-dir: Directory containing JSONL transcript files");
|
|
245
|
+
console.error(" database-path: Path to SQLite database (default: ./search.db)");
|
|
246
|
+
console.error("");
|
|
247
|
+
console.error("Environment variables:");
|
|
248
|
+
console.error(" EMBED_SOCKET: Path to embedding server Unix socket");
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
runIndexer({
|
|
252
|
+
sourceDir,
|
|
253
|
+
databasePath,
|
|
254
|
+
embedSocketPath,
|
|
255
|
+
verbose: true,
|
|
256
|
+
})
|
|
257
|
+
.then(() => {
|
|
258
|
+
closeDatabase();
|
|
259
|
+
process.exit(0);
|
|
260
|
+
})
|
|
261
|
+
.catch((err) => {
|
|
262
|
+
console.error("Indexing failed:", err);
|
|
263
|
+
closeDatabase();
|
|
264
|
+
process.exit(1);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAY,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAW,MAAM,MAAM,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAe,MAAM,gBAAgB,CAAC;AACtG,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjG,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAW,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAmB,MAAM,yBAAyB,CAAC;AAmBjF;;GAEG;AACH,SAAS,cAAc,CAAC,GAAW;IACjC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,SAAS,IAAI,CAAC,UAAkB;QAC9B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACjE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC9C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;oBACxB,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACjB,CAAC;qBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC3D,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,iCAAiC;QACnC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAAC,QAAgB;IAC7C,OAAO,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc,CAAC,QAAgB,EAAE,SAAiB;IACzD,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACxE,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,eAAe,CAC5B,QAAgB,EAChB,SAAiB,EACjB,eAAiC,EACjC,OAAiB;IAEjB,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;IACzB,MAAM,cAAc,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAEpD,IAAI,CAAC;QACH,uBAAuB;QACvB,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC3C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACvB,CAAC;QAED,oBAAoB;QACpB,MAAM,WAAW,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QAErC,wCAAwC;QACxC,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,UAAU,CAAC;QAEhE,iEAAiE;QACjE,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAE3C,yEAAyE;QACzE,MAAM,QAAQ,GAAG,eAAe,CAAC,cAAc,CAAC,CAAC;QACjD,IAAI,QAAQ,EAAE,CAAC;YACb,kBAAkB,CAAC,cAAc,CAAC,CAAC;QACrC,CAAC;QAED,sBAAsB;QACtB,kBAAkB,CAAC;YACjB,EAAE,EAAE,cAAc;YAClB,OAAO;YACP,KAAK;YACL,UAAU,EAAE,SAAS;YACrB,SAAS,EAAE,QAAQ;YACnB,YAAY,EAAE,WAAW;YACzB,YAAY,EAAE,KAAK;SACpB,CAAC,CAAC;QAEH,+BAA+B;QAC/B,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE;gBACxC,SAAS,EAAE,GAAG;gBACd,OAAO,EAAE,EAAE;aACZ,CAAC,CAAC;YAEH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACvC,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC7B,IAAI,SAAS,GAAkB,IAAI,CAAC;gBAEpC,oCAAoC;gBACpC,IAAI,eAAe,EAAE,CAAC;oBACpB,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;oBACvD,IAAI,MAAM,EAAE,CAAC;wBACX,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;oBACrE,CAAC;gBACH,CAAC;gBAED,WAAW,CAAC;oBACV,eAAe,EAAE,cAAc;oBAC/B,WAAW,EAAE,UAAU;oBACvB,WAAW,EAAE,UAAU;oBACvB,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,OAAO,EAAE,UAAU;oBACnB,SAAS;iBACV,CAAC,CAAC;gBAEH,UAAU,EAAE,CAAC;YACf,CAAC;YAED,iEAAiE;YACjE,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC;gBAC5C,UAAU,EAAE,CAAC;YACf,CAAC;QACH,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,CAAC,aAAa,cAAc,KAAK,UAAU,SAAS,CAAC,CAAC;QACnE,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/D,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAqB;IACpD,MAAM,EACJ,SAAS,EACT,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,SAAS,GAAG,EAAE,EACd,OAAO,GAAG,KAAK,GAChB,GAAG,OAAO,CAAC;IAEZ,MAAM,KAAK,GAAe;QACxB,KAAK,EAAE,CAAC;QACR,QAAQ,EAAE,CAAC;QACX,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,CAAC;QACT,MAAM,EAAE,EAAE;KACX,CAAC;IAEF,sBAAsB;IACtB,cAAc,CAAC,YAAY,CAAC,CAAC;IAC7B,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;IAEzB,yEAAyE;IACzE,IAAI,eAA4C,CAAC;IACjD,MAAM,aAAa,GAAG,QAAQ,IAAI,eAAe,CAAC;IAClD,IAAI,aAAa,EAAE,CAAC;QAClB,eAAe,GAAG,qBAAqB,CAAC,aAAa,CAAC,CAAC;QACvD,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,SAAS,EAAE,CAAC;QAClD,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,YAAY,EAAE,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,oCAAoC,aAAa,YAAY,IAAI,EAAE,KAAK,UAAU,IAAI,EAAE,GAAG,GAAG,CAAC,CAAC;QAC9G,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,qCAAqC,aAAa,gCAAgC,CAAC,CAAC;YAChG,eAAe,GAAG,SAAS,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,MAAM,KAAK,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC,MAAM,mBAAmB,SAAS,EAAE,CAAC,CAAC;IAEjE,iBAAiB;IACjB,MAAM,OAAO,GAAG,EAAE;SACf,OAAO,CAAC,qEAAqE,CAAC;SAC9E,GAAG,EAKJ,CAAC;IAEH,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAE/B,qCAAqC;IACrC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;aAAM,CAAC;YACN,oBAAoB;YACpB,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,KAAK,KAAK,QAAQ,CAAC,YAAY,EAAE,CAAC;gBACpC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;gBAC/B,IAAI,IAAI,KAAK,QAAQ,CAAC,YAAY,EAAE,CAAC;oBACnC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACnB,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;QAC1C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACvB,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC1B,kBAAkB,CAAC,EAAE,CAAC,CAAC;QACvB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,2BAA2B;IAC3B,OAAO,CAAC,GAAG,CAAC,YAAY,OAAO,CAAC,MAAM,WAAW,KAAK,CAAC,KAAK,SAAS,KAAK,CAAC,QAAQ,YAAY,CAAC,CAAC;IAEjG,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,CAAC,CAAC;QAEhF,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC;QAChC,CAAC;QAED,qBAAqB;QACrB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,kBAAkB;IAClB,WAAW,CAAC,iBAAiB,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IACzD,WAAW,CAAC,qBAAqB,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAEzD,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/C,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kBAAkB;AAClB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;IACnF,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC;IACtD,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAEjD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,kEAAkE,CAAC,CAAC;QAClF,OAAO,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC3E,OAAO,CAAC,KAAK,CAAC,iEAAiE,CAAC,CAAC;QACjF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QACxC,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;QACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,UAAU,CAAC;QACT,SAAS;QACT,YAAY;QACZ,eAAe;QACf,OAAO,EAAE,IAAI;KACd,CAAC;SACC,IAAI,CAAC,GAAG,EAAE;QACT,aAAa,EAAE,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;QACvC,aAAa,EAAE,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface Message {
|
|
2
|
+
index: number;
|
|
3
|
+
role: 'user' | 'assistant';
|
|
4
|
+
content: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ConversationMetadata {
|
|
7
|
+
messageCount: number;
|
|
8
|
+
userMessageCount: number;
|
|
9
|
+
assistantMessageCount: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function parseTranscript(path: string): Message[];
|
|
12
|
+
export declare function extractConversationMetadata(path: string): ConversationMetadata;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
export function parseTranscript(path) {
|
|
3
|
+
const lines = readFileSync(path, 'utf-8').trim().split('\n').filter(Boolean);
|
|
4
|
+
const messages = [];
|
|
5
|
+
let idx = 0;
|
|
6
|
+
for (const line of lines) {
|
|
7
|
+
try {
|
|
8
|
+
const p = JSON.parse(line);
|
|
9
|
+
if (p.type === 'user' && p.message?.role === 'user') {
|
|
10
|
+
const text = extractText(p.message.content);
|
|
11
|
+
if (text)
|
|
12
|
+
messages.push({ index: idx++, role: 'user', content: text });
|
|
13
|
+
}
|
|
14
|
+
else if (p.type === 'assistant' && p.message?.role === 'assistant') {
|
|
15
|
+
const text = extractText(p.message.content);
|
|
16
|
+
if (text)
|
|
17
|
+
messages.push({ index: idx++, role: 'assistant', content: text });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Skip malformed JSON lines
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return messages;
|
|
25
|
+
}
|
|
26
|
+
function extractText(content) {
|
|
27
|
+
if (typeof content === 'string')
|
|
28
|
+
return content;
|
|
29
|
+
if (Array.isArray(content)) {
|
|
30
|
+
return content
|
|
31
|
+
.filter((b) => b.type === 'text' && typeof b.text === 'string')
|
|
32
|
+
.map(b => b.text)
|
|
33
|
+
.join('\n');
|
|
34
|
+
}
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
export function extractConversationMetadata(path) {
|
|
38
|
+
const messages = parseTranscript(path);
|
|
39
|
+
return {
|
|
40
|
+
messageCount: messages.length,
|
|
41
|
+
userMessageCount: messages.filter(m => m.role === 'user').length,
|
|
42
|
+
assistantMessageCount: messages.filter(m => m.role === 'assistant').length,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/indexer/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAyBlC,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7E,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,GAAG,GAAG,CAAC,CAAC;IAEZ,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAE3B,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,OAAO,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;gBACpD,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC5C,IAAI,IAAI;oBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACzE,CAAC;iBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,OAAO,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;gBACrE,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC5C,IAAI,IAAI;oBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,4BAA4B;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,WAAW,CAAC,OAAgC;IACnD,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAO,OAAO;aACX,MAAM,CAAC,CAAC,CAAC,EAAkB,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;aAC9E,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAChB,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,IAAY;IACtD,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO;QACL,YAAY,EAAE,QAAQ,CAAC,MAAM;QAC7B,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,MAAM;QAChE,qBAAqB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,MAAM;KAC3E,CAAC;AACJ,CAAC"}
|
package/dist/server.d.ts
ADDED