chattercatcher 0.1.3 → 0.1.6
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/dist/cli.js +180 -238
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +6 -12
- package/dist/index.js +169 -245
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
package/dist/cli.js
CHANGED
|
@@ -1,155 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __esm = (fn, res) => function __init() {
|
|
5
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
-
};
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// src/config/paths.ts
|
|
13
|
-
import os2 from "os";
|
|
14
|
-
import path2 from "path";
|
|
15
|
-
function getChatterCatcherHome() {
|
|
16
|
-
return process.env.CHATTERCATCHER_HOME || path2.join(os2.homedir(), ".chattercatcher");
|
|
17
|
-
}
|
|
18
|
-
function resolveHomePath(value) {
|
|
19
|
-
if (value === "~") {
|
|
20
|
-
return os2.homedir();
|
|
21
|
-
}
|
|
22
|
-
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
23
|
-
return path2.join(os2.homedir(), value.slice(2));
|
|
24
|
-
}
|
|
25
|
-
return path2.resolve(value);
|
|
26
|
-
}
|
|
27
|
-
function getConfigPath() {
|
|
28
|
-
return path2.join(getChatterCatcherHome(), "config.json");
|
|
29
|
-
}
|
|
30
|
-
function getSecretsPath() {
|
|
31
|
-
return path2.join(getChatterCatcherHome(), "secrets.json");
|
|
32
|
-
}
|
|
33
|
-
var init_paths = __esm({
|
|
34
|
-
"src/config/paths.ts"() {
|
|
35
|
-
"use strict";
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// src/rag/lancedb-store.ts
|
|
40
|
-
var lancedb_store_exports = {};
|
|
41
|
-
__export(lancedb_store_exports, {
|
|
42
|
-
LanceDbVectorStore: () => LanceDbVectorStore,
|
|
43
|
-
getLanceDbPath: () => getLanceDbPath
|
|
44
|
-
});
|
|
45
|
-
import fs5 from "fs/promises";
|
|
46
|
-
import path8 from "path";
|
|
47
|
-
function getLanceDbPath(config) {
|
|
48
|
-
return path8.join(resolveHomePath(config.storage.dataDir), "vector", "lancedb");
|
|
49
|
-
}
|
|
50
|
-
function toRow(record) {
|
|
51
|
-
return {
|
|
52
|
-
id: record.id,
|
|
53
|
-
vector: record.vector,
|
|
54
|
-
text: record.evidence.text,
|
|
55
|
-
source_json: JSON.stringify(record.evidence.source)
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
function toLanceData(rows) {
|
|
59
|
-
return rows.map((row) => ({
|
|
60
|
-
id: row.id,
|
|
61
|
-
vector: row.vector,
|
|
62
|
-
text: row.text,
|
|
63
|
-
source_json: row.source_json
|
|
64
|
-
}));
|
|
65
|
-
}
|
|
66
|
-
function escapeSqlString(value) {
|
|
67
|
-
return value.replace(/'/g, "''");
|
|
68
|
-
}
|
|
69
|
-
function toEvidence(row) {
|
|
70
|
-
const distance = row._distance ?? 0;
|
|
71
|
-
const vectorScore = 1 / (1 + Math.max(0, distance));
|
|
72
|
-
return {
|
|
73
|
-
id: row.id,
|
|
74
|
-
text: row.text,
|
|
75
|
-
score: vectorScore,
|
|
76
|
-
vectorScore,
|
|
77
|
-
source: JSON.parse(row.source_json)
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
var DEFAULT_TABLE_NAME, LanceDbVectorStore;
|
|
81
|
-
var init_lancedb_store = __esm({
|
|
82
|
-
"src/rag/lancedb-store.ts"() {
|
|
83
|
-
"use strict";
|
|
84
|
-
init_paths();
|
|
85
|
-
DEFAULT_TABLE_NAME = "message_chunks";
|
|
86
|
-
LanceDbVectorStore = class _LanceDbVectorStore {
|
|
87
|
-
constructor(connection, tableName) {
|
|
88
|
-
this.connection = connection;
|
|
89
|
-
this.tableName = tableName;
|
|
90
|
-
}
|
|
91
|
-
connection;
|
|
92
|
-
tableName;
|
|
93
|
-
static async connect(uri, tableName = DEFAULT_TABLE_NAME) {
|
|
94
|
-
await fs5.mkdir(uri, { recursive: true });
|
|
95
|
-
const lancedb = await import("@lancedb/lancedb");
|
|
96
|
-
const connection = await lancedb.connect(uri);
|
|
97
|
-
return new _LanceDbVectorStore(connection, tableName);
|
|
98
|
-
}
|
|
99
|
-
static async connectFromConfig(config, tableName = DEFAULT_TABLE_NAME) {
|
|
100
|
-
return _LanceDbVectorStore.connect(getLanceDbPath(config), tableName);
|
|
101
|
-
}
|
|
102
|
-
close() {
|
|
103
|
-
this.connection.close();
|
|
104
|
-
}
|
|
105
|
-
async upsert(records) {
|
|
106
|
-
if (records.length === 0) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
const rows = records.map(toRow);
|
|
110
|
-
const data2 = toLanceData(rows);
|
|
111
|
-
const table = await this.ensureTable(data2);
|
|
112
|
-
const ids = rows.map((row) => `'${escapeSqlString(row.id)}'`).join(", ");
|
|
113
|
-
await table.delete(`id IN (${ids})`);
|
|
114
|
-
await table.add(data2);
|
|
115
|
-
}
|
|
116
|
-
async search(vector, limit) {
|
|
117
|
-
const table = await this.openTableIfExists();
|
|
118
|
-
if (!table) {
|
|
119
|
-
return [];
|
|
120
|
-
}
|
|
121
|
-
const rows = await table.vectorSearch(vector).limit(limit).toArray();
|
|
122
|
-
return rows.map(toEvidence);
|
|
123
|
-
}
|
|
124
|
-
async count() {
|
|
125
|
-
const table = await this.openTableIfExists();
|
|
126
|
-
if (!table) {
|
|
127
|
-
return 0;
|
|
128
|
-
}
|
|
129
|
-
return table.countRows();
|
|
130
|
-
}
|
|
131
|
-
async ensureTable(initialRows) {
|
|
132
|
-
const table = await this.openTableIfExists();
|
|
133
|
-
if (table) {
|
|
134
|
-
return table;
|
|
135
|
-
}
|
|
136
|
-
return this.connection.createTable(this.tableName, initialRows);
|
|
137
|
-
}
|
|
138
|
-
async openTableIfExists() {
|
|
139
|
-
const tableNames = await this.connection.tableNames();
|
|
140
|
-
if (!tableNames.includes(this.tableName)) {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
return this.connection.openTable(this.tableName);
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
2
|
|
|
149
3
|
// src/cli.ts
|
|
150
4
|
import { input, password, select, confirm, number } from "@inquirer/prompts";
|
|
151
5
|
import { Command } from "commander";
|
|
152
|
-
import
|
|
6
|
+
import fs12 from "fs/promises";
|
|
153
7
|
|
|
154
8
|
// src/config/store.ts
|
|
155
9
|
import fs from "fs/promises";
|
|
@@ -218,8 +72,29 @@ function createDefaultSecrets() {
|
|
|
218
72
|
});
|
|
219
73
|
}
|
|
220
74
|
|
|
75
|
+
// src/config/paths.ts
|
|
76
|
+
import os2 from "os";
|
|
77
|
+
import path2 from "path";
|
|
78
|
+
function getChatterCatcherHome() {
|
|
79
|
+
return process.env.CHATTERCATCHER_HOME || path2.join(os2.homedir(), ".chattercatcher");
|
|
80
|
+
}
|
|
81
|
+
function resolveHomePath(value) {
|
|
82
|
+
if (value === "~") {
|
|
83
|
+
return os2.homedir();
|
|
84
|
+
}
|
|
85
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
86
|
+
return path2.join(os2.homedir(), value.slice(2));
|
|
87
|
+
}
|
|
88
|
+
return path2.resolve(value);
|
|
89
|
+
}
|
|
90
|
+
function getConfigPath() {
|
|
91
|
+
return path2.join(getChatterCatcherHome(), "config.json");
|
|
92
|
+
}
|
|
93
|
+
function getSecretsPath() {
|
|
94
|
+
return path2.join(getChatterCatcherHome(), "secrets.json");
|
|
95
|
+
}
|
|
96
|
+
|
|
221
97
|
// src/config/store.ts
|
|
222
|
-
init_paths();
|
|
223
98
|
async function readJsonFile(filePath, fallback) {
|
|
224
99
|
try {
|
|
225
100
|
const raw = await fs.readFile(filePath, "utf8");
|
|
@@ -282,11 +157,7 @@ function resolveEmbeddingApiKey(input2) {
|
|
|
282
157
|
return explicit || input2.llmApiKey;
|
|
283
158
|
}
|
|
284
159
|
|
|
285
|
-
// src/cli.ts
|
|
286
|
-
init_paths();
|
|
287
|
-
|
|
288
160
|
// src/data/deletion.ts
|
|
289
|
-
init_paths();
|
|
290
161
|
import fs2 from "fs/promises";
|
|
291
162
|
import path4 from "path";
|
|
292
163
|
function emptyResult(targetType, targetId) {
|
|
@@ -412,7 +283,6 @@ async function deleteLocalData(input2) {
|
|
|
412
283
|
}
|
|
413
284
|
|
|
414
285
|
// src/db/database.ts
|
|
415
|
-
init_paths();
|
|
416
286
|
import Database from "better-sqlite3";
|
|
417
287
|
import fs3 from "fs";
|
|
418
288
|
import path5 from "path";
|
|
@@ -473,6 +343,13 @@ function migrateDatabase(database) {
|
|
|
473
343
|
tokenize = 'unicode61'
|
|
474
344
|
);
|
|
475
345
|
|
|
346
|
+
CREATE TABLE IF NOT EXISTS message_chunk_vectors (
|
|
347
|
+
chunk_id TEXT PRIMARY KEY REFERENCES message_chunks(id) ON DELETE CASCADE,
|
|
348
|
+
vector_json TEXT NOT NULL,
|
|
349
|
+
evidence_json TEXT NOT NULL,
|
|
350
|
+
updated_at TEXT NOT NULL
|
|
351
|
+
);
|
|
352
|
+
|
|
476
353
|
CREATE TABLE IF NOT EXISTS file_jobs (
|
|
477
354
|
id TEXT PRIMARY KEY,
|
|
478
355
|
source_path TEXT NOT NULL,
|
|
@@ -492,8 +369,7 @@ function migrateDatabase(database) {
|
|
|
492
369
|
}
|
|
493
370
|
|
|
494
371
|
// src/doctor/checks.ts
|
|
495
|
-
|
|
496
|
-
import fs6 from "fs/promises";
|
|
372
|
+
import fs5 from "fs/promises";
|
|
497
373
|
|
|
498
374
|
// src/files/jobs.ts
|
|
499
375
|
import crypto from "crypto";
|
|
@@ -635,7 +511,6 @@ var FileJobRepository = class {
|
|
|
635
511
|
};
|
|
636
512
|
|
|
637
513
|
// src/gateway/runtime.ts
|
|
638
|
-
init_paths();
|
|
639
514
|
import fs4 from "fs";
|
|
640
515
|
import path7 from "path";
|
|
641
516
|
function getGatewayPidPath() {
|
|
@@ -1265,6 +1140,82 @@ var MessageFtsRetriever = class {
|
|
|
1265
1140
|
}
|
|
1266
1141
|
};
|
|
1267
1142
|
|
|
1143
|
+
// src/rag/embedding.ts
|
|
1144
|
+
function cosineSimilarity(left, right) {
|
|
1145
|
+
if (left.length === 0 || right.length === 0 || left.length !== right.length) {
|
|
1146
|
+
return 0;
|
|
1147
|
+
}
|
|
1148
|
+
let dot = 0;
|
|
1149
|
+
let leftNorm = 0;
|
|
1150
|
+
let rightNorm = 0;
|
|
1151
|
+
for (let index2 = 0; index2 < left.length; index2 += 1) {
|
|
1152
|
+
const leftValue = left[index2] ?? 0;
|
|
1153
|
+
const rightValue = right[index2] ?? 0;
|
|
1154
|
+
dot += leftValue * rightValue;
|
|
1155
|
+
leftNorm += leftValue * leftValue;
|
|
1156
|
+
rightNorm += rightValue * rightValue;
|
|
1157
|
+
}
|
|
1158
|
+
if (leftNorm === 0 || rightNorm === 0) {
|
|
1159
|
+
return 0;
|
|
1160
|
+
}
|
|
1161
|
+
return dot / (Math.sqrt(leftNorm) * Math.sqrt(rightNorm));
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// src/rag/sqlite-vector-store.ts
|
|
1165
|
+
var SQLiteVectorStore = class {
|
|
1166
|
+
constructor(database) {
|
|
1167
|
+
this.database = database;
|
|
1168
|
+
}
|
|
1169
|
+
database;
|
|
1170
|
+
async upsert(records) {
|
|
1171
|
+
if (records.length === 0) {
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const statement = this.database.prepare(`
|
|
1175
|
+
INSERT INTO message_chunk_vectors (chunk_id, vector_json, evidence_json, updated_at)
|
|
1176
|
+
VALUES (@chunkId, @vectorJson, @evidenceJson, @updatedAt)
|
|
1177
|
+
ON CONFLICT(chunk_id) DO UPDATE SET
|
|
1178
|
+
vector_json = excluded.vector_json,
|
|
1179
|
+
evidence_json = excluded.evidence_json,
|
|
1180
|
+
updated_at = excluded.updated_at
|
|
1181
|
+
`);
|
|
1182
|
+
const upsertMany = this.database.transaction((items) => {
|
|
1183
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1184
|
+
for (const item of items) {
|
|
1185
|
+
statement.run({
|
|
1186
|
+
chunkId: item.id,
|
|
1187
|
+
vectorJson: JSON.stringify(item.vector),
|
|
1188
|
+
evidenceJson: JSON.stringify(item.evidence),
|
|
1189
|
+
updatedAt
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
upsertMany(records);
|
|
1194
|
+
}
|
|
1195
|
+
async search(vector, limit) {
|
|
1196
|
+
if (limit <= 0) {
|
|
1197
|
+
return [];
|
|
1198
|
+
}
|
|
1199
|
+
const rows = this.database.prepare("SELECT chunk_id, vector_json, evidence_json FROM message_chunk_vectors").all();
|
|
1200
|
+
return rows.map((row) => {
|
|
1201
|
+
const storedVector = JSON.parse(row.vector_json);
|
|
1202
|
+
const evidence = JSON.parse(row.evidence_json);
|
|
1203
|
+
const vectorScore = cosineSimilarity(vector, storedVector);
|
|
1204
|
+
return {
|
|
1205
|
+
...evidence,
|
|
1206
|
+
score: vectorScore,
|
|
1207
|
+
vectorScore
|
|
1208
|
+
};
|
|
1209
|
+
}).sort((left, right) => right.vectorScore - left.vectorScore).slice(0, limit);
|
|
1210
|
+
}
|
|
1211
|
+
async count() {
|
|
1212
|
+
const row = this.database.prepare("SELECT COUNT(*) AS count FROM message_chunk_vectors").get();
|
|
1213
|
+
return row.count;
|
|
1214
|
+
}
|
|
1215
|
+
close() {
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1268
1219
|
// src/rag/vector-retriever.ts
|
|
1269
1220
|
var VectorRetriever = class {
|
|
1270
1221
|
constructor(embedding, store, limit = 8) {
|
|
@@ -1289,8 +1240,7 @@ async function createHybridRetriever(input2) {
|
|
|
1289
1240
|
const retrievers = [new MessageFtsRetriever(input2.messages, { excludeMessageIds: input2.excludeMessageIds })];
|
|
1290
1241
|
const closers = [];
|
|
1291
1242
|
if (hasEmbeddingConfig(input2.config, input2.secrets)) {
|
|
1292
|
-
const
|
|
1293
|
-
const vectorStore = await LanceDbVectorStore2.connectFromConfig(input2.config);
|
|
1243
|
+
const vectorStore = new SQLiteVectorStore(input2.messages.database);
|
|
1294
1244
|
retrievers.push(new VectorRetriever(createEmbeddingModel(input2.config, input2.secrets), vectorStore));
|
|
1295
1245
|
closers.push(() => vectorStore.close());
|
|
1296
1246
|
}
|
|
@@ -1322,7 +1272,7 @@ async function runDoctor(config, secrets, options = {}) {
|
|
|
1322
1272
|
checks.push(checkEmbeddingConfig(config, secrets));
|
|
1323
1273
|
checks.push(await checkSqlite(config));
|
|
1324
1274
|
checks.push(await checkFilePipeline(config));
|
|
1325
|
-
checks.push(await
|
|
1275
|
+
checks.push(await checkSqliteVector(config));
|
|
1326
1276
|
checks.push(checkRagPolicy());
|
|
1327
1277
|
if (options.online) {
|
|
1328
1278
|
checks.push(await checkChatModel(config, secrets));
|
|
@@ -1333,8 +1283,8 @@ async function runDoctor(config, secrets, options = {}) {
|
|
|
1333
1283
|
async function checkHomeDirectory() {
|
|
1334
1284
|
const home = getChatterCatcherHome();
|
|
1335
1285
|
try {
|
|
1336
|
-
await
|
|
1337
|
-
await
|
|
1286
|
+
await fs5.mkdir(home, { recursive: true });
|
|
1287
|
+
await fs5.access(home);
|
|
1338
1288
|
return pass("\u914D\u7F6E\u76EE\u5F55", home);
|
|
1339
1289
|
} catch (error) {
|
|
1340
1290
|
return fail("\u914D\u7F6E\u76EE\u5F55", error instanceof Error ? error.message : String(error));
|
|
@@ -1355,7 +1305,7 @@ function checkLlmConfig(config, secrets) {
|
|
|
1355
1305
|
}
|
|
1356
1306
|
function checkEmbeddingConfig(config, secrets) {
|
|
1357
1307
|
if (!hasEmbeddingConfig(config, secrets)) {
|
|
1358
|
-
return warn("Embedding \u914D\u7F6E", "\u672A\u914D\u7F6E\u5B8C\u6574\uFF1BRAG \u4F1A\u4F7F\u7528 SQLite FTS\uFF0C\u65E0\u6CD5\u4F7F\u7528
|
|
1308
|
+
return warn("Embedding \u914D\u7F6E", "\u672A\u914D\u7F6E\u5B8C\u6574\uFF1BRAG \u4F1A\u4F7F\u7528 SQLite FTS\uFF0C\u65E0\u6CD5\u4F7F\u7528 SQLite Vector \u8BED\u4E49\u68C0\u7D22\u3002");
|
|
1359
1309
|
}
|
|
1360
1310
|
return pass("Embedding \u914D\u7F6E", `${config.embedding.model} @ ${config.embedding.baseUrl || config.llm.baseUrl}`);
|
|
1361
1311
|
}
|
|
@@ -1389,17 +1339,17 @@ async function checkFilePipeline(config) {
|
|
|
1389
1339
|
database?.close();
|
|
1390
1340
|
}
|
|
1391
1341
|
}
|
|
1392
|
-
async function
|
|
1393
|
-
let
|
|
1342
|
+
async function checkSqliteVector(config) {
|
|
1343
|
+
let database = null;
|
|
1394
1344
|
try {
|
|
1395
|
-
|
|
1396
|
-
store =
|
|
1345
|
+
database = openDatabase(config);
|
|
1346
|
+
const store = new SQLiteVectorStore(database);
|
|
1397
1347
|
const count = await store.count();
|
|
1398
|
-
return pass("
|
|
1348
|
+
return pass("SQLite Vector", `${getDatabasePath(config)}\uFF1Bvectors=${count}`);
|
|
1399
1349
|
} catch (error) {
|
|
1400
|
-
return fail("
|
|
1350
|
+
return fail("SQLite Vector", error instanceof Error ? error.message : String(error));
|
|
1401
1351
|
} finally {
|
|
1402
|
-
|
|
1352
|
+
database?.close();
|
|
1403
1353
|
}
|
|
1404
1354
|
}
|
|
1405
1355
|
function checkRagPolicy() {
|
|
@@ -1440,9 +1390,8 @@ function formatDoctorChecks(checks) {
|
|
|
1440
1390
|
}
|
|
1441
1391
|
|
|
1442
1392
|
// src/export/data-export.ts
|
|
1443
|
-
|
|
1444
|
-
import
|
|
1445
|
-
import path9 from "path";
|
|
1393
|
+
import fs6 from "fs/promises";
|
|
1394
|
+
import path8 from "path";
|
|
1446
1395
|
function parseJsonObject(value) {
|
|
1447
1396
|
try {
|
|
1448
1397
|
const parsed = JSON.parse(value);
|
|
@@ -1461,11 +1410,11 @@ function parseJsonArray(value) {
|
|
|
1461
1410
|
}
|
|
1462
1411
|
function defaultExportPath(config, exportedAt) {
|
|
1463
1412
|
const fileName = `chattercatcher-export-${exportedAt.replace(/[:.]/g, "-")}.json`;
|
|
1464
|
-
return
|
|
1413
|
+
return path8.join(resolveHomePath(config.storage.dataDir), "exports", fileName);
|
|
1465
1414
|
}
|
|
1466
1415
|
async function exportLocalData(input2) {
|
|
1467
1416
|
const exportedAt = input2.exportedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1468
|
-
const outputPath =
|
|
1417
|
+
const outputPath = path8.resolve(input2.outputPath ?? defaultExportPath(input2.config, exportedAt));
|
|
1469
1418
|
const chats = input2.database.prepare(
|
|
1470
1419
|
`
|
|
1471
1420
|
SELECT
|
|
@@ -1552,8 +1501,8 @@ async function exportLocalData(input2) {
|
|
|
1552
1501
|
fileJobs
|
|
1553
1502
|
}
|
|
1554
1503
|
};
|
|
1555
|
-
await
|
|
1556
|
-
await
|
|
1504
|
+
await fs6.mkdir(path8.dirname(outputPath), { recursive: true });
|
|
1505
|
+
await fs6.writeFile(outputPath, `${JSON.stringify(payload, null, 2)}
|
|
1557
1506
|
`, "utf8");
|
|
1558
1507
|
return {
|
|
1559
1508
|
outputPath,
|
|
@@ -1565,8 +1514,8 @@ async function exportLocalData(input2) {
|
|
|
1565
1514
|
}
|
|
1566
1515
|
|
|
1567
1516
|
// src/export/data-restore.ts
|
|
1568
|
-
import
|
|
1569
|
-
import
|
|
1517
|
+
import fs7 from "fs/promises";
|
|
1518
|
+
import path9 from "path";
|
|
1570
1519
|
function asObject(value) {
|
|
1571
1520
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1572
1521
|
}
|
|
@@ -1614,8 +1563,8 @@ function clearDatabase(database) {
|
|
|
1614
1563
|
database.prepare("DELETE FROM chats").run();
|
|
1615
1564
|
}
|
|
1616
1565
|
async function restoreLocalData(input2) {
|
|
1617
|
-
const inputPath =
|
|
1618
|
-
const payload = parsePayload(await
|
|
1566
|
+
const inputPath = path9.resolve(input2.inputPath);
|
|
1567
|
+
const payload = parsePayload(await fs7.readFile(inputPath, "utf8"));
|
|
1619
1568
|
const mode = input2.replace ? "replace" : "merge";
|
|
1620
1569
|
const restore = input2.database.transaction(() => {
|
|
1621
1570
|
if (input2.replace) {
|
|
@@ -2207,10 +2156,9 @@ function createFeishuGateway(options) {
|
|
|
2207
2156
|
}
|
|
2208
2157
|
|
|
2209
2158
|
// src/feishu/resource-downloader.ts
|
|
2210
|
-
init_paths();
|
|
2211
2159
|
import * as lark3 from "@larksuiteoapi/node-sdk";
|
|
2212
|
-
import
|
|
2213
|
-
import
|
|
2160
|
+
import fs8 from "fs/promises";
|
|
2161
|
+
import path10 from "path";
|
|
2214
2162
|
var RESOURCE_TYPE_BY_KIND = {
|
|
2215
2163
|
file: "file",
|
|
2216
2164
|
image: "image",
|
|
@@ -2248,10 +2196,10 @@ var FeishuResourceDownloader = class _FeishuResourceDownloader {
|
|
|
2248
2196
|
}
|
|
2249
2197
|
async download(input2) {
|
|
2250
2198
|
const resourceType = RESOURCE_TYPE_BY_KIND[input2.attachment.kind];
|
|
2251
|
-
const targetDir =
|
|
2252
|
-
await
|
|
2199
|
+
const targetDir = path10.join(this.dataDir, "files", "feishu");
|
|
2200
|
+
await fs8.mkdir(targetDir, { recursive: true });
|
|
2253
2201
|
const fileName = buildStoredFileName(input2);
|
|
2254
|
-
const storedPath =
|
|
2202
|
+
const storedPath = path10.join(targetDir, fileName);
|
|
2255
2203
|
const payload = {
|
|
2256
2204
|
params: { type: resourceType },
|
|
2257
2205
|
path: { message_id: input2.messageId, file_key: input2.attachment.fileKey }
|
|
@@ -2273,31 +2221,30 @@ var FeishuResourceDownloader = class _FeishuResourceDownloader {
|
|
|
2273
2221
|
};
|
|
2274
2222
|
|
|
2275
2223
|
// src/files/ingest.ts
|
|
2276
|
-
init_paths();
|
|
2277
2224
|
import crypto3 from "crypto";
|
|
2278
|
-
import fs11 from "fs/promises";
|
|
2279
|
-
import path13 from "path";
|
|
2280
|
-
|
|
2281
|
-
// src/files/parser.ts
|
|
2282
2225
|
import fs10 from "fs/promises";
|
|
2283
2226
|
import path12 from "path";
|
|
2227
|
+
|
|
2228
|
+
// src/files/parser.ts
|
|
2229
|
+
import fs9 from "fs/promises";
|
|
2230
|
+
import path11 from "path";
|
|
2284
2231
|
import mammoth from "mammoth";
|
|
2285
2232
|
import { PDFParse } from "pdf-parse";
|
|
2286
2233
|
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".txt", ".md", ".markdown", ".json", ".csv", ".tsv", ".log"]);
|
|
2287
2234
|
var DOCX_EXTENSIONS = /* @__PURE__ */ new Set([".docx"]);
|
|
2288
2235
|
var PDF_EXTENSIONS = /* @__PURE__ */ new Set([".pdf"]);
|
|
2289
2236
|
function isSupportedParseFile(filePath) {
|
|
2290
|
-
const extension =
|
|
2237
|
+
const extension = path11.extname(filePath).toLowerCase();
|
|
2291
2238
|
return TEXT_EXTENSIONS.has(extension) || DOCX_EXTENSIONS.has(extension) || PDF_EXTENSIONS.has(extension);
|
|
2292
2239
|
}
|
|
2293
2240
|
function describeSupportedParseTypes() {
|
|
2294
2241
|
return "txt\u3001md\u3001json\u3001csv\u3001tsv\u3001log\u3001docx\u3001pdf";
|
|
2295
2242
|
}
|
|
2296
2243
|
async function parseFileToText(filePath) {
|
|
2297
|
-
const extension =
|
|
2244
|
+
const extension = path11.extname(filePath).toLowerCase();
|
|
2298
2245
|
if (TEXT_EXTENSIONS.has(extension)) {
|
|
2299
2246
|
return {
|
|
2300
|
-
text: await
|
|
2247
|
+
text: await fs9.readFile(filePath, "utf8"),
|
|
2301
2248
|
parser: "text",
|
|
2302
2249
|
warnings: []
|
|
2303
2250
|
};
|
|
@@ -2311,7 +2258,7 @@ async function parseFileToText(filePath) {
|
|
|
2311
2258
|
};
|
|
2312
2259
|
}
|
|
2313
2260
|
if (PDF_EXTENSIONS.has(extension)) {
|
|
2314
|
-
const buffer = await
|
|
2261
|
+
const buffer = await fs9.readFile(filePath);
|
|
2315
2262
|
const parser = new PDFParse({ data: buffer });
|
|
2316
2263
|
try {
|
|
2317
2264
|
const result = await parser.getText();
|
|
@@ -2333,7 +2280,7 @@ function isSupportedTextFile(filePath) {
|
|
|
2333
2280
|
}
|
|
2334
2281
|
function ensureSupportedTextFile(filePath) {
|
|
2335
2282
|
if (!isSupportedTextFile(filePath)) {
|
|
2336
|
-
const extension =
|
|
2283
|
+
const extension = path12.extname(filePath).toLowerCase();
|
|
2337
2284
|
throw new Error(`\u6682\u4E0D\u652F\u6301\u8BE5\u6587\u4EF6\u7C7B\u578B\uFF1A${extension || "\u65E0\u6269\u5C55\u540D"}\u3002\u5F53\u524D\u652F\u6301 ${describeSupportedParseTypes()}\u3002`);
|
|
2338
2285
|
}
|
|
2339
2286
|
}
|
|
@@ -2342,12 +2289,12 @@ function stableStoredName(sourcePath, fileName) {
|
|
|
2342
2289
|
return `${digest}-${fileName}`;
|
|
2343
2290
|
}
|
|
2344
2291
|
async function ingestLocalFile(input2) {
|
|
2345
|
-
const sourcePath =
|
|
2346
|
-
const fileName =
|
|
2292
|
+
const sourcePath = path12.resolve(input2.filePath);
|
|
2293
|
+
const fileName = path12.basename(sourcePath);
|
|
2347
2294
|
const jobId = input2.jobs?.start({ sourcePath, fileName });
|
|
2348
2295
|
try {
|
|
2349
2296
|
ensureSupportedTextFile(sourcePath);
|
|
2350
|
-
const stat = await
|
|
2297
|
+
const stat = await fs10.stat(sourcePath);
|
|
2351
2298
|
if (!stat.isFile()) {
|
|
2352
2299
|
throw new Error(`\u4E0D\u662F\u6587\u4EF6\uFF1A${sourcePath}`);
|
|
2353
2300
|
}
|
|
@@ -2356,10 +2303,10 @@ async function ingestLocalFile(input2) {
|
|
|
2356
2303
|
if (!text) {
|
|
2357
2304
|
throw new Error(`\u6587\u4EF6\u6CA1\u6709\u53EF\u7D22\u5F15\u6587\u672C\uFF1A${sourcePath}`);
|
|
2358
2305
|
}
|
|
2359
|
-
const fileDir =
|
|
2360
|
-
await
|
|
2361
|
-
const storedPath =
|
|
2362
|
-
await
|
|
2306
|
+
const fileDir = path12.join(resolveHomePath(input2.config.storage.dataDir), "files");
|
|
2307
|
+
await fs10.mkdir(fileDir, { recursive: true });
|
|
2308
|
+
const storedPath = path12.join(fileDir, stableStoredName(sourcePath, fileName));
|
|
2309
|
+
await fs10.copyFile(sourcePath, storedPath);
|
|
2363
2310
|
const messageId = input2.messages.ingest({
|
|
2364
2311
|
platform: "local-file",
|
|
2365
2312
|
platformChatId: "local-files",
|
|
@@ -2641,15 +2588,14 @@ var GatewayIngestor = class {
|
|
|
2641
2588
|
};
|
|
2642
2589
|
|
|
2643
2590
|
// src/logs/reader.ts
|
|
2644
|
-
|
|
2645
|
-
import fs12 from "fs/promises";
|
|
2591
|
+
import fs11 from "fs/promises";
|
|
2646
2592
|
import { watch } from "fs";
|
|
2647
|
-
import
|
|
2593
|
+
import path13 from "path";
|
|
2648
2594
|
function getLogsDirectory() {
|
|
2649
|
-
return
|
|
2595
|
+
return path13.join(getChatterCatcherHome(), "logs");
|
|
2650
2596
|
}
|
|
2651
2597
|
function resolveLogPath(fileName, logsDir = getLogsDirectory()) {
|
|
2652
|
-
return
|
|
2598
|
+
return path13.isAbsolute(fileName) ? fileName : path13.join(logsDir, fileName);
|
|
2653
2599
|
}
|
|
2654
2600
|
function normalizeLineCount(value, fallback = 200) {
|
|
2655
2601
|
const parsed = Number(value ?? fallback);
|
|
@@ -2658,7 +2604,7 @@ function normalizeLineCount(value, fallback = 200) {
|
|
|
2658
2604
|
async function listLogFiles(logsDir = getLogsDirectory()) {
|
|
2659
2605
|
let entries;
|
|
2660
2606
|
try {
|
|
2661
|
-
entries = await
|
|
2607
|
+
entries = await fs11.readdir(logsDir, { withFileTypes: true });
|
|
2662
2608
|
} catch (error) {
|
|
2663
2609
|
if (error.code === "ENOENT") {
|
|
2664
2610
|
return [];
|
|
@@ -2667,8 +2613,8 @@ async function listLogFiles(logsDir = getLogsDirectory()) {
|
|
|
2667
2613
|
}
|
|
2668
2614
|
const files2 = await Promise.all(
|
|
2669
2615
|
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".log")).map(async (entry) => {
|
|
2670
|
-
const filePath =
|
|
2671
|
-
const stats = await
|
|
2616
|
+
const filePath = path13.join(logsDir, entry.name);
|
|
2617
|
+
const stats = await fs11.stat(filePath);
|
|
2672
2618
|
return {
|
|
2673
2619
|
name: entry.name,
|
|
2674
2620
|
path: filePath,
|
|
@@ -2685,11 +2631,11 @@ function tailLines(content, lines) {
|
|
|
2685
2631
|
return parts.slice(-lines).join("\n");
|
|
2686
2632
|
}
|
|
2687
2633
|
async function readLogTail(input2) {
|
|
2688
|
-
const stats = await
|
|
2689
|
-
const content = await
|
|
2634
|
+
const stats = await fs11.stat(input2.filePath);
|
|
2635
|
+
const content = await fs11.readFile(input2.filePath, "utf8");
|
|
2690
2636
|
return {
|
|
2691
2637
|
file: {
|
|
2692
|
-
name:
|
|
2638
|
+
name: path13.basename(input2.filePath),
|
|
2693
2639
|
path: input2.filePath,
|
|
2694
2640
|
updatedAt: stats.mtime,
|
|
2695
2641
|
bytes: stats.size
|
|
@@ -2711,18 +2657,18 @@ async function readLatestLogTail(input2 = {}) {
|
|
|
2711
2657
|
return readLogTail({ filePath: latest.path, lines: input2.lines });
|
|
2712
2658
|
}
|
|
2713
2659
|
async function followLogFile(input2) {
|
|
2714
|
-
let offset = (await
|
|
2715
|
-
const directory =
|
|
2716
|
-
const fileName =
|
|
2660
|
+
let offset = (await fs11.stat(input2.filePath)).size;
|
|
2661
|
+
const directory = path13.dirname(input2.filePath);
|
|
2662
|
+
const fileName = path13.basename(input2.filePath);
|
|
2717
2663
|
async function readAppended() {
|
|
2718
|
-
const stats = await
|
|
2664
|
+
const stats = await fs11.stat(input2.filePath);
|
|
2719
2665
|
if (stats.size < offset) {
|
|
2720
2666
|
offset = 0;
|
|
2721
2667
|
}
|
|
2722
2668
|
if (stats.size === offset) {
|
|
2723
2669
|
return;
|
|
2724
2670
|
}
|
|
2725
|
-
const handle = await
|
|
2671
|
+
const handle = await fs11.open(input2.filePath, "r");
|
|
2726
2672
|
try {
|
|
2727
2673
|
const length = stats.size - offset;
|
|
2728
2674
|
const buffer = Buffer.alloc(length);
|
|
@@ -2803,8 +2749,7 @@ async function processMessagesNow(input2) {
|
|
|
2803
2749
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2804
2750
|
};
|
|
2805
2751
|
}
|
|
2806
|
-
const
|
|
2807
|
-
const vectorStore = await LanceDbVectorStore2.connectFromConfig(input2.config);
|
|
2752
|
+
const vectorStore = new SQLiteVectorStore(input2.database);
|
|
2808
2753
|
try {
|
|
2809
2754
|
const stats = await indexMessageChunks({
|
|
2810
2755
|
messages: new MessageRepository(input2.database),
|
|
@@ -3221,7 +3166,7 @@ function createWebApp(config) {
|
|
|
3221
3166
|
note: "\u95EE\u7B54\u5FC5\u987B\u5148\u68C0\u7D22\u8BC1\u636E\uFF0C\u7981\u6B62\u5168\u91CF\u4E0A\u4E0B\u6587\u5806\u53E0\u3002",
|
|
3222
3167
|
retrieval: {
|
|
3223
3168
|
keyword: "SQLite FTS5",
|
|
3224
|
-
vector: "
|
|
3169
|
+
vector: "SQLite Vector",
|
|
3225
3170
|
hybrid: true
|
|
3226
3171
|
}
|
|
3227
3172
|
},
|
|
@@ -3341,7 +3286,7 @@ function printSettings(config, secrets) {
|
|
|
3341
3286
|
2
|
|
3342
3287
|
));
|
|
3343
3288
|
}
|
|
3344
|
-
program.name("chattercatcher").description("\u672C\u5730\u4F18\u5148\u7684\u98DE\u4E66/Lark \u5BB6\u5EAD\u7FA4\u77E5\u8BC6\u673A\u5668\u4EBA").version("0.1.
|
|
3289
|
+
program.name("chattercatcher").description("\u672C\u5730\u4F18\u5148\u7684\u98DE\u4E66/Lark \u5BB6\u5EAD\u7FA4\u77E5\u8BC6\u673A\u5668\u4EBA").version("0.1.6");
|
|
3345
3290
|
program.command("setup").description("\u4EA4\u4E92\u5F0F\u521D\u59CB\u5316\u914D\u7F6E").action(async () => {
|
|
3346
3291
|
const { config, secrets } = await ensureConfigFiles();
|
|
3347
3292
|
await promptForConfiguration(config, secrets);
|
|
@@ -3389,8 +3334,7 @@ async function startGatewayCommand() {
|
|
|
3389
3334
|
return;
|
|
3390
3335
|
}
|
|
3391
3336
|
const database = openDatabase(config);
|
|
3392
|
-
const
|
|
3393
|
-
const vectorStore = LanceDbVectorStore2 ? await LanceDbVectorStore2.connectFromConfig(config) : null;
|
|
3337
|
+
const vectorStore = hasEmbeddingConfig(config, secrets) ? new SQLiteVectorStore(database) : null;
|
|
3394
3338
|
const gatewayRuntime = createFeishuGateway({
|
|
3395
3339
|
config,
|
|
3396
3340
|
secrets,
|
|
@@ -3475,7 +3419,7 @@ async function deleteDataCommand(targetType, targetId, options) {
|
|
|
3475
3419
|
if (result.skippedStoredFiles.length > 0) {
|
|
3476
3420
|
console.log(`\u8DF3\u8FC7\u975E\u6570\u636E\u76EE\u5F55\u6587\u4EF6\uFF1A${result.skippedStoredFiles.join("\uFF1B")}`);
|
|
3477
3421
|
}
|
|
3478
|
-
console.log("SQLite FTS \u5DF2\u540C\u6B65\u5220\u9664\uFF1B\u5982\u4F7F\u7528
|
|
3422
|
+
console.log("SQLite FTS \u5DF2\u540C\u6B65\u5220\u9664\uFF1B\u5982\u4F7F\u7528\u8BED\u4E49\u68C0\u7D22\uFF0C\u8BF7\u8FD0\u884C chattercatcher index rebuild \u91CD\u5EFA SQLite Vector\u3002");
|
|
3479
3423
|
} finally {
|
|
3480
3424
|
database.close();
|
|
3481
3425
|
}
|
|
@@ -3490,20 +3434,19 @@ index.command("status").description("\u67E5\u770B\u7D22\u5F15\u72B6\u6001").acti
|
|
|
3490
3434
|
const secrets = await loadSecrets();
|
|
3491
3435
|
const database = openDatabase(config);
|
|
3492
3436
|
const messages = new MessageRepository(database);
|
|
3493
|
-
const
|
|
3494
|
-
const vectorStore = await LanceDbVectorStore2.connectFromConfig(config);
|
|
3437
|
+
const vectorStore = new SQLiteVectorStore(database);
|
|
3495
3438
|
const vectors = await vectorStore.count();
|
|
3496
3439
|
console.log(JSON.stringify(
|
|
3497
3440
|
{
|
|
3498
3441
|
database: getDatabasePath(config),
|
|
3499
|
-
vectorDatabase:
|
|
3442
|
+
vectorDatabase: getDatabasePath(config),
|
|
3500
3443
|
chats: messages.getChatCount(),
|
|
3501
3444
|
messages: messages.getMessageCount(),
|
|
3502
3445
|
vectors,
|
|
3503
3446
|
retrieval: {
|
|
3504
3447
|
keyword: "SQLite FTS5",
|
|
3505
|
-
vector: hasEmbeddingConfig(config, secrets) ? "
|
|
3506
|
-
hybrid: "\u542F\u7528\uFF1ASQLite FTS +
|
|
3448
|
+
vector: hasEmbeddingConfig(config, secrets) ? "SQLite Vector \u5DF2\u53EF\u7528\u4E8E\u8BED\u4E49\u68C0\u7D22" : "SQLite Vector \u5DF2\u63A5\u5165\uFF1B\u9700\u914D\u7F6E embedding \u540E\u542F\u7528\u8BED\u4E49\u68C0\u7D22",
|
|
3449
|
+
hybrid: "\u542F\u7528\uFF1ASQLite FTS + SQLite Vector",
|
|
3507
3450
|
rag: "\u5F3A\u5236\u5148\u68C0\u7D22\u8BC1\u636E\u518D\u56DE\u7B54\uFF0C\u7981\u6B62\u5168\u91CF\u4E0A\u4E0B\u6587\u5806\u53E0"
|
|
3508
3451
|
}
|
|
3509
3452
|
},
|
|
@@ -3513,7 +3456,7 @@ index.command("status").description("\u67E5\u770B\u7D22\u5F15\u72B6\u6001").acti
|
|
|
3513
3456
|
vectorStore.close();
|
|
3514
3457
|
database.close();
|
|
3515
3458
|
});
|
|
3516
|
-
index.command("rebuild").description("\u91CD\u5EFA
|
|
3459
|
+
index.command("rebuild").description("\u91CD\u5EFA SQLite \u5411\u91CF\u7D22\u5F15").option("--limit <number>", "\u6700\u591A\u7D22\u5F15\u7684 chunk \u6570", "10000").action(async (options) => {
|
|
3517
3460
|
const config = await loadConfig();
|
|
3518
3461
|
const secrets = await loadSecrets();
|
|
3519
3462
|
if (!hasEmbeddingConfig(config, secrets)) {
|
|
@@ -3521,8 +3464,7 @@ index.command("rebuild").description("\u91CD\u5EFA LanceDB \u5411\u91CF\u7D22\u5
|
|
|
3521
3464
|
return;
|
|
3522
3465
|
}
|
|
3523
3466
|
const database = openDatabase(config);
|
|
3524
|
-
const
|
|
3525
|
-
const vectorStore = await LanceDbVectorStore2.connectFromConfig(config);
|
|
3467
|
+
const vectorStore = new SQLiteVectorStore(database);
|
|
3526
3468
|
try {
|
|
3527
3469
|
const stats = await indexMessageChunks({
|
|
3528
3470
|
messages: new MessageRepository(database),
|
|
@@ -3537,7 +3479,7 @@ index.command("rebuild").description("\u91CD\u5EFA LanceDB \u5411\u91CF\u7D22\u5
|
|
|
3537
3479
|
}
|
|
3538
3480
|
});
|
|
3539
3481
|
var processCommand = program.command("process").description("\u7ACB\u5373\u5904\u7406\u540E\u53F0\u4EFB\u52A1");
|
|
3540
|
-
processCommand.command("messages").description("\u7ACB\u5373\u5904\u7406\u6D88\u606F\u7D22\u5F15\u4EFB\u52A1\uFF0C\u628A\u6D88\u606F chunks \u5199\u5165
|
|
3482
|
+
processCommand.command("messages").description("\u7ACB\u5373\u5904\u7406\u6D88\u606F\u7D22\u5F15\u4EFB\u52A1\uFF0C\u628A\u6D88\u606F chunks \u5199\u5165 SQLite \u5411\u91CF\u7D22\u5F15").option("--limit <number>", "\u6700\u591A\u5904\u7406\u7684 chunk \u6570", "10000").action(async (options) => {
|
|
3541
3483
|
const config = await loadConfig();
|
|
3542
3484
|
const secrets = await loadSecrets();
|
|
3543
3485
|
const database = openDatabase(config);
|
|
@@ -3571,7 +3513,7 @@ files.command("add").description("\u628A\u672C\u5730\u6587\u4EF6\u89E3\u6790\u30
|
|
|
3571
3513
|
`\u5DF2\u5BFC\u5165\u6587\u4EF6\uFF1A${result.fileName}\uFF0C\u89E3\u6790\u5668=${result.parser}\uFF0C\u5B57\u7B26\u6570=${result.characters}\uFF0C\u6D88\u606FID=${result.messageId}`
|
|
3572
3514
|
);
|
|
3573
3515
|
}
|
|
3574
|
-
console.log("\u6587\u4EF6\u5DF2\u8FDB\u5165 SQLite FTS \u68C0\u7D22\uFF1B\u5982\u5DF2\u914D\u7F6E embedding\uFF0C\u53EF\u8FD0\u884C chattercatcher index rebuild \u66F4\u65B0
|
|
3516
|
+
console.log("\u6587\u4EF6\u5DF2\u8FDB\u5165 SQLite FTS \u68C0\u7D22\uFF1B\u5982\u5DF2\u914D\u7F6E embedding\uFF0C\u53EF\u8FD0\u884C chattercatcher index rebuild \u66F4\u65B0 SQLite \u5411\u91CF\u7D22\u5F15\u3002");
|
|
3575
3517
|
} finally {
|
|
3576
3518
|
database.close();
|
|
3577
3519
|
}
|
|
@@ -3700,7 +3642,7 @@ program.command("restore").description("\u4ECE ChatterCatcher \u5BFC\u51FA\u6587
|
|
|
3700
3642
|
console.log(`\u6062\u590D\u5B8C\u6210\uFF1A${result.inputPath}`);
|
|
3701
3643
|
console.log(`\u6A21\u5F0F\uFF1A${result.mode === "replace" ? "\u66FF\u6362" : "\u5408\u5E76"}`);
|
|
3702
3644
|
console.log(`\u5305\u542B\uFF1A\u7FA4\u804A=${result.chats}\uFF0C\u6D88\u606F=${result.messages}\uFF0Cchunks=${result.chunks}\uFF0C\u6587\u4EF6\u4EFB\u52A1=${result.fileJobs}`);
|
|
3703
|
-
console.log("SQLite FTS \u5DF2\u91CD\u5EFA\uFF1B\u5982\u4F7F\u7528
|
|
3645
|
+
console.log("SQLite FTS \u5DF2\u91CD\u5EFA\uFF1B\u5982\u4F7F\u7528\u8BED\u4E49\u68C0\u7D22\uFF0C\u8BF7\u8FD0\u884C chattercatcher index rebuild \u91CD\u5EFA SQLite Vector\u3002");
|
|
3704
3646
|
} finally {
|
|
3705
3647
|
database.close();
|
|
3706
3648
|
}
|
|
@@ -3730,7 +3672,7 @@ dev.command("ingest-feishu-event").description("\u4ECE JSON \u6587\u4EF6\u6A21\u
|
|
|
3730
3672
|
const config = await loadConfig();
|
|
3731
3673
|
const database = openDatabase(config);
|
|
3732
3674
|
try {
|
|
3733
|
-
const raw = await
|
|
3675
|
+
const raw = await fs12.readFile(options.file, "utf8");
|
|
3734
3676
|
const payload = JSON.parse(raw);
|
|
3735
3677
|
const result = new GatewayIngestor(database).ingestFeishuEvent(payload);
|
|
3736
3678
|
if (!result.accepted) {
|