chattercatcher 0.1.6 → 0.1.8
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 +649 -277
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +15 -6
- package/dist/index.js +367 -270
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,224 @@
|
|
|
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 fs6 from "fs/promises";
|
|
46
|
+
import path9 from "path";
|
|
47
|
+
function getLanceDbPath(config) {
|
|
48
|
+
return path9.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 fs6.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
|
+
});
|
|
2
148
|
|
|
3
149
|
// src/cli.ts
|
|
4
150
|
import { input, password, select, confirm, number } from "@inquirer/prompts";
|
|
5
151
|
import { Command } from "commander";
|
|
6
|
-
import
|
|
152
|
+
import fs14 from "fs/promises";
|
|
153
|
+
|
|
154
|
+
// package.json
|
|
155
|
+
var package_default = {
|
|
156
|
+
name: "chattercatcher",
|
|
157
|
+
version: "0.1.8",
|
|
158
|
+
description: "\u672C\u5730\u4F18\u5148\u7684\u98DE\u4E66/Lark \u5BB6\u5EAD\u7FA4\u77E5\u8BC6\u5E93\u673A\u5668\u4EBA",
|
|
159
|
+
type: "module",
|
|
160
|
+
main: "dist/index.js",
|
|
161
|
+
types: "dist/index.d.ts",
|
|
162
|
+
homepage: "https://github.com/FlashingChen2024/chattercatcher#readme",
|
|
163
|
+
repository: {
|
|
164
|
+
type: "git",
|
|
165
|
+
url: "git+https://github.com/FlashingChen2024/chattercatcher.git"
|
|
166
|
+
},
|
|
167
|
+
bugs: {
|
|
168
|
+
url: "https://github.com/FlashingChen2024/chattercatcher/issues"
|
|
169
|
+
},
|
|
170
|
+
bin: {
|
|
171
|
+
chattercatcher: "dist/cli.js"
|
|
172
|
+
},
|
|
173
|
+
files: [
|
|
174
|
+
"assets",
|
|
175
|
+
"dist",
|
|
176
|
+
"docs",
|
|
177
|
+
"README.md",
|
|
178
|
+
"AGENTS.md"
|
|
179
|
+
],
|
|
180
|
+
directories: {
|
|
181
|
+
doc: "docs"
|
|
182
|
+
},
|
|
183
|
+
scripts: {
|
|
184
|
+
build: "tsup",
|
|
185
|
+
dev: "tsx src/cli.ts",
|
|
186
|
+
lint: "tsc --noEmit",
|
|
187
|
+
typecheck: "tsc --noEmit",
|
|
188
|
+
test: "vitest run"
|
|
189
|
+
},
|
|
190
|
+
keywords: [
|
|
191
|
+
"feishu",
|
|
192
|
+
"lark",
|
|
193
|
+
"rag",
|
|
194
|
+
"local-first",
|
|
195
|
+
"knowledge-base"
|
|
196
|
+
],
|
|
197
|
+
author: "FlashingChen2024",
|
|
198
|
+
license: "MIT",
|
|
199
|
+
dependencies: {
|
|
200
|
+
"@inquirer/prompts": "^8.4.2",
|
|
201
|
+
"@lancedb/lancedb": "0.23.0",
|
|
202
|
+
"@larksuiteoapi/node-sdk": "^1.62.0",
|
|
203
|
+
"better-sqlite3": "^12.9.0",
|
|
204
|
+
commander: "^14.0.3",
|
|
205
|
+
fastify: "^5.8.5",
|
|
206
|
+
mammoth: "^1.12.0",
|
|
207
|
+
"pdf-parse": "^2.4.5",
|
|
208
|
+
pino: "^10.3.1",
|
|
209
|
+
zod: "^4.3.6"
|
|
210
|
+
},
|
|
211
|
+
devDependencies: {
|
|
212
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
213
|
+
"@types/node": "^25.6.0",
|
|
214
|
+
"@types/yazl": "^3.3.1",
|
|
215
|
+
tsup: "^8.5.1",
|
|
216
|
+
tsx: "^4.21.0",
|
|
217
|
+
typescript: "^6.0.3",
|
|
218
|
+
vitest: "^4.1.5",
|
|
219
|
+
yazl: "^3.3.1"
|
|
220
|
+
}
|
|
221
|
+
};
|
|
7
222
|
|
|
8
223
|
// src/config/store.ts
|
|
9
224
|
import fs from "fs/promises";
|
|
@@ -72,29 +287,8 @@ function createDefaultSecrets() {
|
|
|
72
287
|
});
|
|
73
288
|
}
|
|
74
289
|
|
|
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
|
-
|
|
97
290
|
// src/config/store.ts
|
|
291
|
+
init_paths();
|
|
98
292
|
async function readJsonFile(filePath, fallback) {
|
|
99
293
|
try {
|
|
100
294
|
const raw = await fs.readFile(filePath, "utf8");
|
|
@@ -157,7 +351,11 @@ function resolveEmbeddingApiKey(input2) {
|
|
|
157
351
|
return explicit || input2.llmApiKey;
|
|
158
352
|
}
|
|
159
353
|
|
|
354
|
+
// src/cli.ts
|
|
355
|
+
init_paths();
|
|
356
|
+
|
|
160
357
|
// src/data/deletion.ts
|
|
358
|
+
init_paths();
|
|
161
359
|
import fs2 from "fs/promises";
|
|
162
360
|
import path4 from "path";
|
|
163
361
|
function emptyResult(targetType, targetId) {
|
|
@@ -283,6 +481,7 @@ async function deleteLocalData(input2) {
|
|
|
283
481
|
}
|
|
284
482
|
|
|
285
483
|
// src/db/database.ts
|
|
484
|
+
init_paths();
|
|
286
485
|
import Database from "better-sqlite3";
|
|
287
486
|
import fs3 from "fs";
|
|
288
487
|
import path5 from "path";
|
|
@@ -343,13 +542,6 @@ function migrateDatabase(database) {
|
|
|
343
542
|
tokenize = 'unicode61'
|
|
344
543
|
);
|
|
345
544
|
|
|
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
|
-
|
|
353
545
|
CREATE TABLE IF NOT EXISTS file_jobs (
|
|
354
546
|
id TEXT PRIMARY KEY,
|
|
355
547
|
source_path TEXT NOT NULL,
|
|
@@ -369,7 +561,8 @@ function migrateDatabase(database) {
|
|
|
369
561
|
}
|
|
370
562
|
|
|
371
563
|
// src/doctor/checks.ts
|
|
372
|
-
|
|
564
|
+
init_paths();
|
|
565
|
+
import fs7 from "fs/promises";
|
|
373
566
|
|
|
374
567
|
// src/files/jobs.ts
|
|
375
568
|
import crypto from "crypto";
|
|
@@ -511,10 +704,120 @@ var FileJobRepository = class {
|
|
|
511
704
|
};
|
|
512
705
|
|
|
513
706
|
// src/gateway/runtime.ts
|
|
514
|
-
|
|
707
|
+
init_paths();
|
|
708
|
+
import fs5 from "fs";
|
|
709
|
+
import path8 from "path";
|
|
710
|
+
|
|
711
|
+
// src/logs/reader.ts
|
|
712
|
+
init_paths();
|
|
713
|
+
import fs4 from "fs/promises";
|
|
714
|
+
import { watch } from "fs";
|
|
515
715
|
import path7 from "path";
|
|
716
|
+
function getLogsDirectory() {
|
|
717
|
+
return path7.join(getChatterCatcherHome(), "logs");
|
|
718
|
+
}
|
|
719
|
+
function resolveLogPath(fileName, logsDir = getLogsDirectory()) {
|
|
720
|
+
return path7.isAbsolute(fileName) ? fileName : path7.join(logsDir, fileName);
|
|
721
|
+
}
|
|
722
|
+
function normalizeLineCount(value, fallback = 200) {
|
|
723
|
+
const parsed = Number(value ?? fallback);
|
|
724
|
+
return Number.isFinite(parsed) ? Math.min(Math.max(Math.trunc(parsed), 1), 1e4) : fallback;
|
|
725
|
+
}
|
|
726
|
+
async function listLogFiles(logsDir = getLogsDirectory()) {
|
|
727
|
+
let entries;
|
|
728
|
+
try {
|
|
729
|
+
entries = await fs4.readdir(logsDir, { withFileTypes: true });
|
|
730
|
+
} catch (error) {
|
|
731
|
+
if (error.code === "ENOENT") {
|
|
732
|
+
return [];
|
|
733
|
+
}
|
|
734
|
+
throw error;
|
|
735
|
+
}
|
|
736
|
+
const files2 = await Promise.all(
|
|
737
|
+
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".log")).map(async (entry) => {
|
|
738
|
+
const filePath = path7.join(logsDir, entry.name);
|
|
739
|
+
const stats = await fs4.stat(filePath);
|
|
740
|
+
return {
|
|
741
|
+
name: entry.name,
|
|
742
|
+
path: filePath,
|
|
743
|
+
updatedAt: stats.mtime,
|
|
744
|
+
bytes: stats.size
|
|
745
|
+
};
|
|
746
|
+
})
|
|
747
|
+
);
|
|
748
|
+
return files2.sort((left, right) => right.updatedAt.getTime() - left.updatedAt.getTime());
|
|
749
|
+
}
|
|
750
|
+
function tailLines(content, lines) {
|
|
751
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
752
|
+
const parts = normalized.endsWith("\n") ? normalized.slice(0, -1).split("\n") : normalized.split("\n");
|
|
753
|
+
return parts.slice(-lines).join("\n");
|
|
754
|
+
}
|
|
755
|
+
async function readLogTail(input2) {
|
|
756
|
+
const stats = await fs4.stat(input2.filePath);
|
|
757
|
+
const content = await fs4.readFile(input2.filePath, "utf8");
|
|
758
|
+
return {
|
|
759
|
+
file: {
|
|
760
|
+
name: path7.basename(input2.filePath),
|
|
761
|
+
path: input2.filePath,
|
|
762
|
+
updatedAt: stats.mtime,
|
|
763
|
+
bytes: stats.size
|
|
764
|
+
},
|
|
765
|
+
content: tailLines(content, normalizeLineCount(input2.lines))
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
async function readLatestLogTail(input2 = {}) {
|
|
769
|
+
if (input2.fileName) {
|
|
770
|
+
return readLogTail({
|
|
771
|
+
filePath: resolveLogPath(input2.fileName, input2.logsDir),
|
|
772
|
+
lines: input2.lines
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
const [latest] = await listLogFiles(input2.logsDir);
|
|
776
|
+
if (!latest) {
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
return readLogTail({ filePath: latest.path, lines: input2.lines });
|
|
780
|
+
}
|
|
781
|
+
async function followLogFile(input2) {
|
|
782
|
+
let offset = (await fs4.stat(input2.filePath)).size;
|
|
783
|
+
const directory = path7.dirname(input2.filePath);
|
|
784
|
+
const fileName = path7.basename(input2.filePath);
|
|
785
|
+
async function readAppended() {
|
|
786
|
+
const stats = await fs4.stat(input2.filePath);
|
|
787
|
+
if (stats.size < offset) {
|
|
788
|
+
offset = 0;
|
|
789
|
+
}
|
|
790
|
+
if (stats.size === offset) {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
const handle = await fs4.open(input2.filePath, "r");
|
|
794
|
+
try {
|
|
795
|
+
const length = stats.size - offset;
|
|
796
|
+
const buffer = Buffer.alloc(length);
|
|
797
|
+
await handle.read(buffer, 0, length, offset);
|
|
798
|
+
offset = stats.size;
|
|
799
|
+
input2.onChunk(buffer.toString("utf8"));
|
|
800
|
+
} finally {
|
|
801
|
+
await handle.close();
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
const watcher = watch(directory, (eventType, changedFileName) => {
|
|
805
|
+
if (eventType !== "change" || changedFileName?.toString() !== fileName) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
void readAppended().catch((error) => {
|
|
809
|
+
input2.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
return () => watcher.close();
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// src/gateway/runtime.ts
|
|
516
816
|
function getGatewayPidPath() {
|
|
517
|
-
return
|
|
817
|
+
return path8.join(getChatterCatcherHome(), "gateway.pid");
|
|
818
|
+
}
|
|
819
|
+
function getGatewayLogPath() {
|
|
820
|
+
return path8.join(getLogsDirectory(), "gateway.log");
|
|
518
821
|
}
|
|
519
822
|
function isProcessRunning(pid) {
|
|
520
823
|
if (!Number.isInteger(pid) || pid <= 0) {
|
|
@@ -529,7 +832,7 @@ function isProcessRunning(pid) {
|
|
|
529
832
|
}
|
|
530
833
|
function readGatewayPidRecord(pidFile = getGatewayPidPath()) {
|
|
531
834
|
try {
|
|
532
|
-
const raw =
|
|
835
|
+
const raw = fs5.readFileSync(pidFile, "utf8");
|
|
533
836
|
const parsed = JSON.parse(raw);
|
|
534
837
|
if (!Number.isInteger(parsed.pid) || typeof parsed.startedAt !== "string" || typeof parsed.command !== "string") {
|
|
535
838
|
return null;
|
|
@@ -541,7 +844,9 @@ function readGatewayPidRecord(pidFile = getGatewayPidPath()) {
|
|
|
541
844
|
return {
|
|
542
845
|
pid,
|
|
543
846
|
startedAt: parsed.startedAt,
|
|
544
|
-
command: parsed.command
|
|
847
|
+
command: parsed.command,
|
|
848
|
+
...typeof parsed.logFile === "string" ? { logFile: parsed.logFile } : {},
|
|
849
|
+
...parsed.mode === "gateway" || parsed.mode === "web" ? { mode: parsed.mode } : {}
|
|
545
850
|
};
|
|
546
851
|
} catch (error) {
|
|
547
852
|
if (error.code === "ENOENT") {
|
|
@@ -555,13 +860,13 @@ function writeGatewayPidRecord(pidFile = getGatewayPidPath(), record = {
|
|
|
555
860
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
556
861
|
command: process.argv.join(" ")
|
|
557
862
|
}) {
|
|
558
|
-
|
|
559
|
-
|
|
863
|
+
fs5.mkdirSync(path8.dirname(pidFile), { recursive: true });
|
|
864
|
+
fs5.writeFileSync(pidFile, `${JSON.stringify(record, null, 2)}
|
|
560
865
|
`, "utf8");
|
|
561
866
|
}
|
|
562
867
|
function removeGatewayPidRecord(pidFile = getGatewayPidPath()) {
|
|
563
868
|
try {
|
|
564
|
-
|
|
869
|
+
fs5.rmSync(pidFile, { force: true });
|
|
565
870
|
} catch {
|
|
566
871
|
}
|
|
567
872
|
}
|
|
@@ -613,6 +918,28 @@ function stopGatewayProcess(pidFile = getGatewayPidPath()) {
|
|
|
613
918
|
|
|
614
919
|
// src/gateway/index.ts
|
|
615
920
|
function getGatewayStatus(config, secrets) {
|
|
921
|
+
const runtime = getGatewayRuntimeState();
|
|
922
|
+
const configured = Boolean(config.feishu.appId && (!secrets || secrets.feishu.appSecret));
|
|
923
|
+
if (runtime.running && runtime.record) {
|
|
924
|
+
if (runtime.record.mode === "web" && !configured) {
|
|
925
|
+
return {
|
|
926
|
+
configured,
|
|
927
|
+
connection: "running",
|
|
928
|
+
message: `\u672C\u5730 Web UI \u8FDB\u7A0B\u6B63\u5728\u8FD0\u884C\uFF1Apid=${runtime.record.pid}\uFF0CstartedAt=${runtime.record.startedAt}\uFF1B\u98DE\u4E66\u914D\u7F6E\u5C1A\u672A\u5B8C\u6210\u3002`,
|
|
929
|
+
pid: runtime.record.pid,
|
|
930
|
+
pidFile: runtime.pidFile,
|
|
931
|
+
logFile: runtime.record.logFile
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
return {
|
|
935
|
+
configured: true,
|
|
936
|
+
connection: "running",
|
|
937
|
+
message: `\u98DE\u4E66 Gateway \u6B63\u5728\u8FD0\u884C\uFF1Apid=${runtime.record.pid}\uFF0CstartedAt=${runtime.record.startedAt}`,
|
|
938
|
+
pid: runtime.record.pid,
|
|
939
|
+
pidFile: runtime.pidFile,
|
|
940
|
+
logFile: runtime.record.logFile
|
|
941
|
+
};
|
|
942
|
+
}
|
|
616
943
|
if (!config.feishu.appId) {
|
|
617
944
|
return {
|
|
618
945
|
configured: false,
|
|
@@ -627,23 +954,14 @@ function getGatewayStatus(config, secrets) {
|
|
|
627
954
|
message: "\u5C1A\u672A\u914D\u7F6E\u98DE\u4E66 App Secret\u3002\u8BF7\u8FD0\u884C chattercatcher setup \u6216 chattercatcher settings\u3002"
|
|
628
955
|
};
|
|
629
956
|
}
|
|
630
|
-
const runtime = getGatewayRuntimeState();
|
|
631
|
-
if (runtime.running && runtime.record) {
|
|
632
|
-
return {
|
|
633
|
-
configured: true,
|
|
634
|
-
connection: "running",
|
|
635
|
-
message: `\u98DE\u4E66 Gateway \u6B63\u5728\u8FD0\u884C\uFF1Apid=${runtime.record.pid}\uFF0CstartedAt=${runtime.record.startedAt}`,
|
|
636
|
-
pid: runtime.record.pid,
|
|
637
|
-
pidFile: runtime.pidFile
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
957
|
if (runtime.stale && runtime.record) {
|
|
641
958
|
return {
|
|
642
959
|
configured: true,
|
|
643
960
|
connection: "ready_for_start",
|
|
644
961
|
message: `\u98DE\u4E66\u957F\u8FDE\u63A5\u914D\u7F6E\u5DF2\u5C31\u7EEA\uFF1B\u53D1\u73B0\u8FC7\u671F PID \u6587\u4EF6\uFF1Apid=${runtime.record.pid}\u3002\u8FD0\u884C chattercatcher gateway start \u4F1A\u8986\u76D6\u8FD0\u884C\u8BB0\u5F55\u3002`,
|
|
645
962
|
pid: runtime.record.pid,
|
|
646
|
-
pidFile: runtime.pidFile
|
|
963
|
+
pidFile: runtime.pidFile,
|
|
964
|
+
logFile: runtime.record.logFile
|
|
647
965
|
};
|
|
648
966
|
}
|
|
649
967
|
return {
|
|
@@ -1140,82 +1458,6 @@ var MessageFtsRetriever = class {
|
|
|
1140
1458
|
}
|
|
1141
1459
|
};
|
|
1142
1460
|
|
|
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
|
-
|
|
1219
1461
|
// src/rag/vector-retriever.ts
|
|
1220
1462
|
var VectorRetriever = class {
|
|
1221
1463
|
constructor(embedding, store, limit = 8) {
|
|
@@ -1240,7 +1482,8 @@ async function createHybridRetriever(input2) {
|
|
|
1240
1482
|
const retrievers = [new MessageFtsRetriever(input2.messages, { excludeMessageIds: input2.excludeMessageIds })];
|
|
1241
1483
|
const closers = [];
|
|
1242
1484
|
if (hasEmbeddingConfig(input2.config, input2.secrets)) {
|
|
1243
|
-
const
|
|
1485
|
+
const { LanceDbVectorStore: LanceDbVectorStore2 } = await Promise.resolve().then(() => (init_lancedb_store(), lancedb_store_exports));
|
|
1486
|
+
const vectorStore = await LanceDbVectorStore2.connectFromConfig(input2.config);
|
|
1244
1487
|
retrievers.push(new VectorRetriever(createEmbeddingModel(input2.config, input2.secrets), vectorStore));
|
|
1245
1488
|
closers.push(() => vectorStore.close());
|
|
1246
1489
|
}
|
|
@@ -1272,7 +1515,7 @@ async function runDoctor(config, secrets, options = {}) {
|
|
|
1272
1515
|
checks.push(checkEmbeddingConfig(config, secrets));
|
|
1273
1516
|
checks.push(await checkSqlite(config));
|
|
1274
1517
|
checks.push(await checkFilePipeline(config));
|
|
1275
|
-
checks.push(await
|
|
1518
|
+
checks.push(await checkLanceDb(config));
|
|
1276
1519
|
checks.push(checkRagPolicy());
|
|
1277
1520
|
if (options.online) {
|
|
1278
1521
|
checks.push(await checkChatModel(config, secrets));
|
|
@@ -1283,8 +1526,8 @@ async function runDoctor(config, secrets, options = {}) {
|
|
|
1283
1526
|
async function checkHomeDirectory() {
|
|
1284
1527
|
const home = getChatterCatcherHome();
|
|
1285
1528
|
try {
|
|
1286
|
-
await
|
|
1287
|
-
await
|
|
1529
|
+
await fs7.mkdir(home, { recursive: true });
|
|
1530
|
+
await fs7.access(home);
|
|
1288
1531
|
return pass("\u914D\u7F6E\u76EE\u5F55", home);
|
|
1289
1532
|
} catch (error) {
|
|
1290
1533
|
return fail("\u914D\u7F6E\u76EE\u5F55", error instanceof Error ? error.message : String(error));
|
|
@@ -1305,7 +1548,7 @@ function checkLlmConfig(config, secrets) {
|
|
|
1305
1548
|
}
|
|
1306
1549
|
function checkEmbeddingConfig(config, secrets) {
|
|
1307
1550
|
if (!hasEmbeddingConfig(config, secrets)) {
|
|
1308
|
-
return warn("Embedding \u914D\u7F6E", "\u672A\u914D\u7F6E\u5B8C\u6574\uFF1BRAG \u4F1A\u4F7F\u7528 SQLite FTS\uFF0C\u65E0\u6CD5\u4F7F\u7528
|
|
1551
|
+
return warn("Embedding \u914D\u7F6E", "\u672A\u914D\u7F6E\u5B8C\u6574\uFF1BRAG \u4F1A\u4F7F\u7528 SQLite FTS\uFF0C\u65E0\u6CD5\u4F7F\u7528 LanceDB \u8BED\u4E49\u68C0\u7D22\u3002");
|
|
1309
1552
|
}
|
|
1310
1553
|
return pass("Embedding \u914D\u7F6E", `${config.embedding.model} @ ${config.embedding.baseUrl || config.llm.baseUrl}`);
|
|
1311
1554
|
}
|
|
@@ -1339,17 +1582,17 @@ async function checkFilePipeline(config) {
|
|
|
1339
1582
|
database?.close();
|
|
1340
1583
|
}
|
|
1341
1584
|
}
|
|
1342
|
-
async function
|
|
1343
|
-
let
|
|
1585
|
+
async function checkLanceDb(config) {
|
|
1586
|
+
let store = null;
|
|
1344
1587
|
try {
|
|
1345
|
-
|
|
1346
|
-
|
|
1588
|
+
const { getLanceDbPath: getLanceDbPath2, LanceDbVectorStore: LanceDbVectorStore2 } = await Promise.resolve().then(() => (init_lancedb_store(), lancedb_store_exports));
|
|
1589
|
+
store = await LanceDbVectorStore2.connectFromConfig(config);
|
|
1347
1590
|
const count = await store.count();
|
|
1348
|
-
return pass("
|
|
1591
|
+
return pass("LanceDB", `${getLanceDbPath2(config)}\uFF1Bvectors=${count}`);
|
|
1349
1592
|
} catch (error) {
|
|
1350
|
-
return fail("
|
|
1593
|
+
return fail("LanceDB", error instanceof Error ? error.message : String(error));
|
|
1351
1594
|
} finally {
|
|
1352
|
-
|
|
1595
|
+
store?.close();
|
|
1353
1596
|
}
|
|
1354
1597
|
}
|
|
1355
1598
|
function checkRagPolicy() {
|
|
@@ -1390,8 +1633,9 @@ function formatDoctorChecks(checks) {
|
|
|
1390
1633
|
}
|
|
1391
1634
|
|
|
1392
1635
|
// src/export/data-export.ts
|
|
1393
|
-
|
|
1394
|
-
import
|
|
1636
|
+
init_paths();
|
|
1637
|
+
import fs8 from "fs/promises";
|
|
1638
|
+
import path10 from "path";
|
|
1395
1639
|
function parseJsonObject(value) {
|
|
1396
1640
|
try {
|
|
1397
1641
|
const parsed = JSON.parse(value);
|
|
@@ -1410,11 +1654,11 @@ function parseJsonArray(value) {
|
|
|
1410
1654
|
}
|
|
1411
1655
|
function defaultExportPath(config, exportedAt) {
|
|
1412
1656
|
const fileName = `chattercatcher-export-${exportedAt.replace(/[:.]/g, "-")}.json`;
|
|
1413
|
-
return
|
|
1657
|
+
return path10.join(resolveHomePath(config.storage.dataDir), "exports", fileName);
|
|
1414
1658
|
}
|
|
1415
1659
|
async function exportLocalData(input2) {
|
|
1416
1660
|
const exportedAt = input2.exportedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1417
|
-
const outputPath =
|
|
1661
|
+
const outputPath = path10.resolve(input2.outputPath ?? defaultExportPath(input2.config, exportedAt));
|
|
1418
1662
|
const chats = input2.database.prepare(
|
|
1419
1663
|
`
|
|
1420
1664
|
SELECT
|
|
@@ -1501,8 +1745,8 @@ async function exportLocalData(input2) {
|
|
|
1501
1745
|
fileJobs
|
|
1502
1746
|
}
|
|
1503
1747
|
};
|
|
1504
|
-
await
|
|
1505
|
-
await
|
|
1748
|
+
await fs8.mkdir(path10.dirname(outputPath), { recursive: true });
|
|
1749
|
+
await fs8.writeFile(outputPath, `${JSON.stringify(payload, null, 2)}
|
|
1506
1750
|
`, "utf8");
|
|
1507
1751
|
return {
|
|
1508
1752
|
outputPath,
|
|
@@ -1514,8 +1758,8 @@ async function exportLocalData(input2) {
|
|
|
1514
1758
|
}
|
|
1515
1759
|
|
|
1516
1760
|
// src/export/data-restore.ts
|
|
1517
|
-
import
|
|
1518
|
-
import
|
|
1761
|
+
import fs9 from "fs/promises";
|
|
1762
|
+
import path11 from "path";
|
|
1519
1763
|
function asObject(value) {
|
|
1520
1764
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1521
1765
|
}
|
|
@@ -1563,8 +1807,8 @@ function clearDatabase(database) {
|
|
|
1563
1807
|
database.prepare("DELETE FROM chats").run();
|
|
1564
1808
|
}
|
|
1565
1809
|
async function restoreLocalData(input2) {
|
|
1566
|
-
const inputPath =
|
|
1567
|
-
const payload = parsePayload(await
|
|
1810
|
+
const inputPath = path11.resolve(input2.inputPath);
|
|
1811
|
+
const payload = parsePayload(await fs9.readFile(inputPath, "utf8"));
|
|
1568
1812
|
const mode = input2.replace ? "replace" : "merge";
|
|
1569
1813
|
const restore = input2.database.transaction(() => {
|
|
1570
1814
|
if (input2.replace) {
|
|
@@ -2156,9 +2400,10 @@ function createFeishuGateway(options) {
|
|
|
2156
2400
|
}
|
|
2157
2401
|
|
|
2158
2402
|
// src/feishu/resource-downloader.ts
|
|
2403
|
+
init_paths();
|
|
2159
2404
|
import * as lark3 from "@larksuiteoapi/node-sdk";
|
|
2160
|
-
import
|
|
2161
|
-
import
|
|
2405
|
+
import fs10 from "fs/promises";
|
|
2406
|
+
import path12 from "path";
|
|
2162
2407
|
var RESOURCE_TYPE_BY_KIND = {
|
|
2163
2408
|
file: "file",
|
|
2164
2409
|
image: "image",
|
|
@@ -2196,10 +2441,10 @@ var FeishuResourceDownloader = class _FeishuResourceDownloader {
|
|
|
2196
2441
|
}
|
|
2197
2442
|
async download(input2) {
|
|
2198
2443
|
const resourceType = RESOURCE_TYPE_BY_KIND[input2.attachment.kind];
|
|
2199
|
-
const targetDir =
|
|
2200
|
-
await
|
|
2444
|
+
const targetDir = path12.join(this.dataDir, "files", "feishu");
|
|
2445
|
+
await fs10.mkdir(targetDir, { recursive: true });
|
|
2201
2446
|
const fileName = buildStoredFileName(input2);
|
|
2202
|
-
const storedPath =
|
|
2447
|
+
const storedPath = path12.join(targetDir, fileName);
|
|
2203
2448
|
const payload = {
|
|
2204
2449
|
params: { type: resourceType },
|
|
2205
2450
|
path: { message_id: input2.messageId, file_key: input2.attachment.fileKey }
|
|
@@ -2221,30 +2466,31 @@ var FeishuResourceDownloader = class _FeishuResourceDownloader {
|
|
|
2221
2466
|
};
|
|
2222
2467
|
|
|
2223
2468
|
// src/files/ingest.ts
|
|
2469
|
+
init_paths();
|
|
2224
2470
|
import crypto3 from "crypto";
|
|
2225
|
-
import
|
|
2226
|
-
import
|
|
2471
|
+
import fs12 from "fs/promises";
|
|
2472
|
+
import path14 from "path";
|
|
2227
2473
|
|
|
2228
2474
|
// src/files/parser.ts
|
|
2229
|
-
import
|
|
2230
|
-
import
|
|
2475
|
+
import fs11 from "fs/promises";
|
|
2476
|
+
import path13 from "path";
|
|
2231
2477
|
import mammoth from "mammoth";
|
|
2232
2478
|
import { PDFParse } from "pdf-parse";
|
|
2233
2479
|
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".txt", ".md", ".markdown", ".json", ".csv", ".tsv", ".log"]);
|
|
2234
2480
|
var DOCX_EXTENSIONS = /* @__PURE__ */ new Set([".docx"]);
|
|
2235
2481
|
var PDF_EXTENSIONS = /* @__PURE__ */ new Set([".pdf"]);
|
|
2236
2482
|
function isSupportedParseFile(filePath) {
|
|
2237
|
-
const extension =
|
|
2483
|
+
const extension = path13.extname(filePath).toLowerCase();
|
|
2238
2484
|
return TEXT_EXTENSIONS.has(extension) || DOCX_EXTENSIONS.has(extension) || PDF_EXTENSIONS.has(extension);
|
|
2239
2485
|
}
|
|
2240
2486
|
function describeSupportedParseTypes() {
|
|
2241
2487
|
return "txt\u3001md\u3001json\u3001csv\u3001tsv\u3001log\u3001docx\u3001pdf";
|
|
2242
2488
|
}
|
|
2243
2489
|
async function parseFileToText(filePath) {
|
|
2244
|
-
const extension =
|
|
2490
|
+
const extension = path13.extname(filePath).toLowerCase();
|
|
2245
2491
|
if (TEXT_EXTENSIONS.has(extension)) {
|
|
2246
2492
|
return {
|
|
2247
|
-
text: await
|
|
2493
|
+
text: await fs11.readFile(filePath, "utf8"),
|
|
2248
2494
|
parser: "text",
|
|
2249
2495
|
warnings: []
|
|
2250
2496
|
};
|
|
@@ -2258,7 +2504,7 @@ async function parseFileToText(filePath) {
|
|
|
2258
2504
|
};
|
|
2259
2505
|
}
|
|
2260
2506
|
if (PDF_EXTENSIONS.has(extension)) {
|
|
2261
|
-
const buffer = await
|
|
2507
|
+
const buffer = await fs11.readFile(filePath);
|
|
2262
2508
|
const parser = new PDFParse({ data: buffer });
|
|
2263
2509
|
try {
|
|
2264
2510
|
const result = await parser.getText();
|
|
@@ -2280,7 +2526,7 @@ function isSupportedTextFile(filePath) {
|
|
|
2280
2526
|
}
|
|
2281
2527
|
function ensureSupportedTextFile(filePath) {
|
|
2282
2528
|
if (!isSupportedTextFile(filePath)) {
|
|
2283
|
-
const extension =
|
|
2529
|
+
const extension = path14.extname(filePath).toLowerCase();
|
|
2284
2530
|
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`);
|
|
2285
2531
|
}
|
|
2286
2532
|
}
|
|
@@ -2289,12 +2535,12 @@ function stableStoredName(sourcePath, fileName) {
|
|
|
2289
2535
|
return `${digest}-${fileName}`;
|
|
2290
2536
|
}
|
|
2291
2537
|
async function ingestLocalFile(input2) {
|
|
2292
|
-
const sourcePath =
|
|
2293
|
-
const fileName =
|
|
2538
|
+
const sourcePath = path14.resolve(input2.filePath);
|
|
2539
|
+
const fileName = path14.basename(sourcePath);
|
|
2294
2540
|
const jobId = input2.jobs?.start({ sourcePath, fileName });
|
|
2295
2541
|
try {
|
|
2296
2542
|
ensureSupportedTextFile(sourcePath);
|
|
2297
|
-
const stat = await
|
|
2543
|
+
const stat = await fs12.stat(sourcePath);
|
|
2298
2544
|
if (!stat.isFile()) {
|
|
2299
2545
|
throw new Error(`\u4E0D\u662F\u6587\u4EF6\uFF1A${sourcePath}`);
|
|
2300
2546
|
}
|
|
@@ -2303,10 +2549,10 @@ async function ingestLocalFile(input2) {
|
|
|
2303
2549
|
if (!text) {
|
|
2304
2550
|
throw new Error(`\u6587\u4EF6\u6CA1\u6709\u53EF\u7D22\u5F15\u6587\u672C\uFF1A${sourcePath}`);
|
|
2305
2551
|
}
|
|
2306
|
-
const fileDir =
|
|
2307
|
-
await
|
|
2308
|
-
const storedPath =
|
|
2309
|
-
await
|
|
2552
|
+
const fileDir = path14.join(resolveHomePath(input2.config.storage.dataDir), "files");
|
|
2553
|
+
await fs12.mkdir(fileDir, { recursive: true });
|
|
2554
|
+
const storedPath = path14.join(fileDir, stableStoredName(sourcePath, fileName));
|
|
2555
|
+
await fs12.copyFile(sourcePath, storedPath);
|
|
2310
2556
|
const messageId = input2.messages.ingest({
|
|
2311
2557
|
platform: "local-file",
|
|
2312
2558
|
platformChatId: "local-files",
|
|
@@ -2587,107 +2833,72 @@ var GatewayIngestor = class {
|
|
|
2587
2833
|
}
|
|
2588
2834
|
};
|
|
2589
2835
|
|
|
2590
|
-
// src/
|
|
2591
|
-
import
|
|
2592
|
-
import
|
|
2593
|
-
import
|
|
2594
|
-
function
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
}
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
return Number.isFinite(parsed) ? Math.min(Math.max(Math.trunc(parsed), 1), 1e4) : fallback;
|
|
2603
|
-
}
|
|
2604
|
-
async function listLogFiles(logsDir = getLogsDirectory()) {
|
|
2605
|
-
let entries;
|
|
2606
|
-
try {
|
|
2607
|
-
entries = await fs11.readdir(logsDir, { withFileTypes: true });
|
|
2608
|
-
} catch (error) {
|
|
2609
|
-
if (error.code === "ENOENT") {
|
|
2610
|
-
return [];
|
|
2611
|
-
}
|
|
2612
|
-
throw error;
|
|
2836
|
+
// src/gateway/detached.ts
|
|
2837
|
+
import { spawn } from "child_process";
|
|
2838
|
+
import fs13 from "fs";
|
|
2839
|
+
import path15 from "path";
|
|
2840
|
+
function buildGatewayForegroundSpawnCommand(argv = process.argv) {
|
|
2841
|
+
const [command = process.execPath, ...rawArgs] = argv;
|
|
2842
|
+
const args = [...rawArgs];
|
|
2843
|
+
while (args.at(-1) === "--foreground") {
|
|
2844
|
+
args.pop();
|
|
2845
|
+
}
|
|
2846
|
+
if (args.at(-1) === "start" && args.at(-2) === "gateway") {
|
|
2847
|
+
args.splice(-2, 2);
|
|
2613
2848
|
}
|
|
2614
|
-
const files2 = await Promise.all(
|
|
2615
|
-
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".log")).map(async (entry) => {
|
|
2616
|
-
const filePath = path13.join(logsDir, entry.name);
|
|
2617
|
-
const stats = await fs11.stat(filePath);
|
|
2618
|
-
return {
|
|
2619
|
-
name: entry.name,
|
|
2620
|
-
path: filePath,
|
|
2621
|
-
updatedAt: stats.mtime,
|
|
2622
|
-
bytes: stats.size
|
|
2623
|
-
};
|
|
2624
|
-
})
|
|
2625
|
-
);
|
|
2626
|
-
return files2.sort((left, right) => right.updatedAt.getTime() - left.updatedAt.getTime());
|
|
2627
|
-
}
|
|
2628
|
-
function tailLines(content, lines) {
|
|
2629
|
-
const normalized = content.replace(/\r\n/g, "\n");
|
|
2630
|
-
const parts = normalized.endsWith("\n") ? normalized.slice(0, -1).split("\n") : normalized.split("\n");
|
|
2631
|
-
return parts.slice(-lines).join("\n");
|
|
2632
|
-
}
|
|
2633
|
-
async function readLogTail(input2) {
|
|
2634
|
-
const stats = await fs11.stat(input2.filePath);
|
|
2635
|
-
const content = await fs11.readFile(input2.filePath, "utf8");
|
|
2636
2849
|
return {
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
path: input2.filePath,
|
|
2640
|
-
updatedAt: stats.mtime,
|
|
2641
|
-
bytes: stats.size
|
|
2642
|
-
},
|
|
2643
|
-
content: tailLines(content, normalizeLineCount(input2.lines))
|
|
2850
|
+
command,
|
|
2851
|
+
args: [...args, "gateway", "start", "--foreground"]
|
|
2644
2852
|
};
|
|
2645
2853
|
}
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2854
|
+
function startDetachedGateway(input2) {
|
|
2855
|
+
const status = getGatewayStatus(input2.config, input2.secrets);
|
|
2856
|
+
const logFile = getGatewayLogPath();
|
|
2857
|
+
if (status.connection === "running") {
|
|
2858
|
+
return {
|
|
2859
|
+
started: false,
|
|
2860
|
+
message: `\u98DE\u4E66 Gateway \u5DF2\u7ECF\u6B63\u5728\u8FD0\u884C\uFF1Apid=${status.pid ?? "unknown"}`,
|
|
2861
|
+
logFile,
|
|
2862
|
+
...status.pid ? { pid: status.pid } : {}
|
|
2863
|
+
};
|
|
2656
2864
|
}
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
let
|
|
2661
|
-
const
|
|
2662
|
-
|
|
2663
|
-
async function readAppended() {
|
|
2664
|
-
const stats = await fs11.stat(input2.filePath);
|
|
2665
|
-
if (stats.size < offset) {
|
|
2666
|
-
offset = 0;
|
|
2667
|
-
}
|
|
2668
|
-
if (stats.size === offset) {
|
|
2865
|
+
fs13.mkdirSync(path15.dirname(logFile), { recursive: true });
|
|
2866
|
+
let out;
|
|
2867
|
+
let err;
|
|
2868
|
+
let stdioClosed = false;
|
|
2869
|
+
const closeStdio = () => {
|
|
2870
|
+
if (stdioClosed) {
|
|
2669
2871
|
return;
|
|
2670
2872
|
}
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
const buffer = Buffer.alloc(length);
|
|
2675
|
-
await handle.read(buffer, 0, length, offset);
|
|
2676
|
-
offset = stats.size;
|
|
2677
|
-
input2.onChunk(buffer.toString("utf8"));
|
|
2678
|
-
} finally {
|
|
2679
|
-
await handle.close();
|
|
2873
|
+
stdioClosed = true;
|
|
2874
|
+
if (typeof out === "number") {
|
|
2875
|
+
fs13.closeSync(out);
|
|
2680
2876
|
}
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
if (eventType !== "change" || changedFileName?.toString() !== fileName) {
|
|
2684
|
-
return;
|
|
2877
|
+
if (typeof err === "number") {
|
|
2878
|
+
fs13.closeSync(err);
|
|
2685
2879
|
}
|
|
2686
|
-
|
|
2687
|
-
|
|
2880
|
+
};
|
|
2881
|
+
try {
|
|
2882
|
+
out = fs13.openSync(logFile, "a");
|
|
2883
|
+
err = fs13.openSync(logFile, "a");
|
|
2884
|
+
const foreground = buildGatewayForegroundSpawnCommand(input2.argv);
|
|
2885
|
+
const child = spawn(foreground.command, foreground.args, {
|
|
2886
|
+
detached: true,
|
|
2887
|
+
stdio: ["ignore", out, err],
|
|
2888
|
+
windowsHide: true
|
|
2688
2889
|
});
|
|
2689
|
-
|
|
2690
|
-
|
|
2890
|
+
closeStdio();
|
|
2891
|
+
child.unref();
|
|
2892
|
+
return {
|
|
2893
|
+
started: true,
|
|
2894
|
+
message: `\u5DF2\u5728\u540E\u53F0\u542F\u52A8\u98DE\u4E66 Gateway\uFF1Apid=${child.pid}`,
|
|
2895
|
+
pid: child.pid,
|
|
2896
|
+
logFile
|
|
2897
|
+
};
|
|
2898
|
+
} catch (error) {
|
|
2899
|
+
closeStdio();
|
|
2900
|
+
throw error;
|
|
2901
|
+
}
|
|
2691
2902
|
}
|
|
2692
2903
|
|
|
2693
2904
|
// src/rag/indexer.ts
|
|
@@ -2749,7 +2960,8 @@ async function processMessagesNow(input2) {
|
|
|
2749
2960
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2750
2961
|
};
|
|
2751
2962
|
}
|
|
2752
|
-
const
|
|
2963
|
+
const { LanceDbVectorStore: LanceDbVectorStore2 } = await Promise.resolve().then(() => (init_lancedb_store(), lancedb_store_exports));
|
|
2964
|
+
const vectorStore = await LanceDbVectorStore2.connectFromConfig(input2.config);
|
|
2753
2965
|
try {
|
|
2754
2966
|
const stats = await indexMessageChunks({
|
|
2755
2967
|
messages: new MessageRepository(input2.database),
|
|
@@ -2769,6 +2981,108 @@ async function processMessagesNow(input2) {
|
|
|
2769
2981
|
}
|
|
2770
2982
|
}
|
|
2771
2983
|
|
|
2984
|
+
// src/update/npm-updater.ts
|
|
2985
|
+
import { execFile } from "child_process";
|
|
2986
|
+
import { promisify } from "util";
|
|
2987
|
+
var execFileAsync = promisify(execFile);
|
|
2988
|
+
var packageName = "chattercatcher";
|
|
2989
|
+
var installArgs = ["install", "-g", `${packageName}@latest`];
|
|
2990
|
+
var latestVersionArgs = ["view", packageName, "version", "--json"];
|
|
2991
|
+
function formatCommand(command, args) {
|
|
2992
|
+
return [command, ...args].join(" ");
|
|
2993
|
+
}
|
|
2994
|
+
function getErrorMessage(error) {
|
|
2995
|
+
if (error instanceof Error) {
|
|
2996
|
+
return error.message;
|
|
2997
|
+
}
|
|
2998
|
+
return String(error);
|
|
2999
|
+
}
|
|
3000
|
+
function parseLatestVersion(stdout) {
|
|
3001
|
+
const trimmed = stdout.trim();
|
|
3002
|
+
if (!trimmed) {
|
|
3003
|
+
throw new Error("npm registry returned an empty version");
|
|
3004
|
+
}
|
|
3005
|
+
let parsed;
|
|
3006
|
+
try {
|
|
3007
|
+
parsed = JSON.parse(trimmed);
|
|
3008
|
+
} catch {
|
|
3009
|
+
throw new Error("npm registry returned an invalid version");
|
|
3010
|
+
}
|
|
3011
|
+
if (typeof parsed !== "string" || !parsed) {
|
|
3012
|
+
throw new Error("npm registry returned an invalid version");
|
|
3013
|
+
}
|
|
3014
|
+
return parsed;
|
|
3015
|
+
}
|
|
3016
|
+
function compareVersions(left, right) {
|
|
3017
|
+
const leftParts = left.split(".").map((part) => Number(part));
|
|
3018
|
+
const rightParts = right.split(".").map((part) => Number(part));
|
|
3019
|
+
const length = Math.max(leftParts.length, rightParts.length);
|
|
3020
|
+
for (let index2 = 0; index2 < length; index2 += 1) {
|
|
3021
|
+
const leftPart = leftParts[index2] ?? 0;
|
|
3022
|
+
const rightPart = rightParts[index2] ?? 0;
|
|
3023
|
+
if (leftPart > rightPart) {
|
|
3024
|
+
return 1;
|
|
3025
|
+
}
|
|
3026
|
+
if (leftPart < rightPart) {
|
|
3027
|
+
return -1;
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
return 0;
|
|
3031
|
+
}
|
|
3032
|
+
async function defaultUpdateCommandRunner(command, args) {
|
|
3033
|
+
const { stdout, stderr } = await execFileAsync(command, args);
|
|
3034
|
+
return { stdout, stderr };
|
|
3035
|
+
}
|
|
3036
|
+
async function updateChatterCatcher(options) {
|
|
3037
|
+
const runner = options.runner ?? defaultUpdateCommandRunner;
|
|
3038
|
+
const command = formatCommand("npm", installArgs);
|
|
3039
|
+
let latestVersion;
|
|
3040
|
+
try {
|
|
3041
|
+
const output = await runner("npm", latestVersionArgs);
|
|
3042
|
+
latestVersion = parseLatestVersion(output.stdout);
|
|
3043
|
+
} catch (error) {
|
|
3044
|
+
return {
|
|
3045
|
+
status: "query-failed",
|
|
3046
|
+
currentVersion: options.currentVersion,
|
|
3047
|
+
command,
|
|
3048
|
+
error: getErrorMessage(error)
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
if (compareVersions(latestVersion, options.currentVersion) <= 0) {
|
|
3052
|
+
return {
|
|
3053
|
+
status: "up-to-date",
|
|
3054
|
+
currentVersion: options.currentVersion,
|
|
3055
|
+
latestVersion,
|
|
3056
|
+
command
|
|
3057
|
+
};
|
|
3058
|
+
}
|
|
3059
|
+
if (options.dryRun) {
|
|
3060
|
+
return {
|
|
3061
|
+
status: "dry-run",
|
|
3062
|
+
currentVersion: options.currentVersion,
|
|
3063
|
+
latestVersion,
|
|
3064
|
+
command
|
|
3065
|
+
};
|
|
3066
|
+
}
|
|
3067
|
+
try {
|
|
3068
|
+
await runner("npm", installArgs);
|
|
3069
|
+
} catch (error) {
|
|
3070
|
+
return {
|
|
3071
|
+
status: "install-failed",
|
|
3072
|
+
currentVersion: options.currentVersion,
|
|
3073
|
+
latestVersion,
|
|
3074
|
+
command,
|
|
3075
|
+
error: getErrorMessage(error)
|
|
3076
|
+
};
|
|
3077
|
+
}
|
|
3078
|
+
return {
|
|
3079
|
+
status: "updated",
|
|
3080
|
+
currentVersion: options.currentVersion,
|
|
3081
|
+
latestVersion,
|
|
3082
|
+
command
|
|
3083
|
+
};
|
|
3084
|
+
}
|
|
3085
|
+
|
|
2772
3086
|
// src/web/server.ts
|
|
2773
3087
|
import Fastify from "fastify";
|
|
2774
3088
|
function buildHtml() {
|
|
@@ -3166,7 +3480,7 @@ function createWebApp(config) {
|
|
|
3166
3480
|
note: "\u95EE\u7B54\u5FC5\u987B\u5148\u68C0\u7D22\u8BC1\u636E\uFF0C\u7981\u6B62\u5168\u91CF\u4E0A\u4E0B\u6587\u5806\u53E0\u3002",
|
|
3167
3481
|
retrieval: {
|
|
3168
3482
|
keyword: "SQLite FTS5",
|
|
3169
|
-
vector: "
|
|
3483
|
+
vector: "LanceDB",
|
|
3170
3484
|
hybrid: true
|
|
3171
3485
|
}
|
|
3172
3486
|
},
|
|
@@ -3286,7 +3600,7 @@ function printSettings(config, secrets) {
|
|
|
3286
3600
|
2
|
|
3287
3601
|
));
|
|
3288
3602
|
}
|
|
3289
|
-
program.name("chattercatcher").description("\u672C\u5730\u4F18\u5148\u7684\u98DE\u4E66/Lark \u5BB6\u5EAD\u7FA4\u77E5\u8BC6\u673A\u5668\u4EBA").version(
|
|
3603
|
+
program.name("chattercatcher").description("\u672C\u5730\u4F18\u5148\u7684\u98DE\u4E66/Lark \u5BB6\u5EAD\u7FA4\u77E5\u8BC6\u673A\u5668\u4EBA").version(package_default.version);
|
|
3290
3604
|
program.command("setup").description("\u4EA4\u4E92\u5F0F\u521D\u59CB\u5316\u914D\u7F6E").action(async () => {
|
|
3291
3605
|
const { config, secrets } = await ensureConfigFiles();
|
|
3292
3606
|
await promptForConfiguration(config, secrets);
|
|
@@ -3322,19 +3636,60 @@ program.command("doctor").description("\u68C0\u67E5\u672C\u5730\u914D\u7F6E\u300
|
|
|
3322
3636
|
const checks = await runDoctor(config, secrets, { online: options.online });
|
|
3323
3637
|
console.log(formatDoctorChecks(checks));
|
|
3324
3638
|
});
|
|
3639
|
+
program.command("update").description("\u5347\u7EA7 ChatterCatcher \u5230 npm \u6700\u65B0\u7248\u672C").option("--dry-run", "\u53EA\u68C0\u67E5\u5E76\u663E\u793A\u5C06\u6267\u884C\u7684\u5347\u7EA7\u547D\u4EE4").action(async (options) => {
|
|
3640
|
+
const result = await updateChatterCatcher({ currentVersion: package_default.version, dryRun: options.dryRun });
|
|
3641
|
+
if (result.status === "up-to-date") {
|
|
3642
|
+
console.log(`ChatterCatcher \u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF1A${result.currentVersion}`);
|
|
3643
|
+
return;
|
|
3644
|
+
}
|
|
3645
|
+
if (result.status === "dry-run") {
|
|
3646
|
+
console.log(`\u5F53\u524D\u7248\u672C\uFF1A${result.currentVersion}`);
|
|
3647
|
+
console.log(`\u6700\u65B0\u7248\u672C\uFF1A${result.latestVersion}`);
|
|
3648
|
+
console.log(`\u5C06\u6267\u884C\uFF1A${result.command}`);
|
|
3649
|
+
return;
|
|
3650
|
+
}
|
|
3651
|
+
if (result.status === "updated") {
|
|
3652
|
+
console.log(`\u5347\u7EA7\u5B8C\u6210\uFF1A${result.currentVersion} -> ${result.latestVersion}`);
|
|
3653
|
+
console.log("\u8BF7\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u6216\u91CD\u65B0\u8FD0\u884C chattercatcher --version \u786E\u8BA4\u7248\u672C\u3002");
|
|
3654
|
+
return;
|
|
3655
|
+
}
|
|
3656
|
+
if (result.status === "query-failed") {
|
|
3657
|
+
console.error(`\u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\uFF1A${result.error}`);
|
|
3658
|
+
process.exitCode = 1;
|
|
3659
|
+
return;
|
|
3660
|
+
}
|
|
3661
|
+
console.error(`\u5347\u7EA7\u5931\u8D25\uFF1A${result.error}`);
|
|
3662
|
+
console.error(`\u53EF\u624B\u52A8\u8FD0\u884C\uFF1A${result.command}`);
|
|
3663
|
+
process.exitCode = 1;
|
|
3664
|
+
});
|
|
3325
3665
|
var gateway = program.command("gateway").description("\u7BA1\u7406\u672C\u5730\u98DE\u4E66 Gateway");
|
|
3326
|
-
async function
|
|
3666
|
+
async function startGatewayForegroundCommand() {
|
|
3327
3667
|
const config = await loadConfig();
|
|
3328
3668
|
const secrets = await loadSecrets();
|
|
3329
3669
|
const status = getGatewayStatus(config, secrets);
|
|
3670
|
+
const pidRecordBase = {
|
|
3671
|
+
pid: process.pid,
|
|
3672
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3673
|
+
command: process.argv.join(" "),
|
|
3674
|
+
logFile: getGatewayLogPath()
|
|
3675
|
+
};
|
|
3330
3676
|
if (!status.configured) {
|
|
3677
|
+
writeGatewayPidRecord(void 0, {
|
|
3678
|
+
...pidRecordBase,
|
|
3679
|
+
mode: "web"
|
|
3680
|
+
});
|
|
3331
3681
|
console.log(status.message);
|
|
3332
3682
|
console.log("\u672C\u5730 Web UI \u4ECD\u4F1A\u542F\u52A8\uFF0C\u65B9\u4FBF\u7EE7\u7EED\u914D\u7F6E\u3002");
|
|
3333
3683
|
await startWebServer(config);
|
|
3334
3684
|
return;
|
|
3335
3685
|
}
|
|
3686
|
+
writeGatewayPidRecord(void 0, {
|
|
3687
|
+
...pidRecordBase,
|
|
3688
|
+
mode: "gateway"
|
|
3689
|
+
});
|
|
3336
3690
|
const database = openDatabase(config);
|
|
3337
|
-
const
|
|
3691
|
+
const { LanceDbVectorStore: LanceDbVectorStore2 } = hasEmbeddingConfig(config, secrets) ? await Promise.resolve().then(() => (init_lancedb_store(), lancedb_store_exports)) : { LanceDbVectorStore: null };
|
|
3692
|
+
const vectorStore = LanceDbVectorStore2 ? await LanceDbVectorStore2.connectFromConfig(config) : null;
|
|
3338
3693
|
const gatewayRuntime = createFeishuGateway({
|
|
3339
3694
|
config,
|
|
3340
3695
|
secrets,
|
|
@@ -3369,7 +3724,6 @@ async function startGatewayCommand() {
|
|
|
3369
3724
|
process.exit(0);
|
|
3370
3725
|
});
|
|
3371
3726
|
console.log(status.message);
|
|
3372
|
-
writeGatewayPidRecord();
|
|
3373
3727
|
try {
|
|
3374
3728
|
await gatewayRuntime.start();
|
|
3375
3729
|
await startWebServer(config);
|
|
@@ -3378,12 +3732,28 @@ async function startGatewayCommand() {
|
|
|
3378
3732
|
throw error;
|
|
3379
3733
|
}
|
|
3380
3734
|
}
|
|
3735
|
+
async function startGatewayCommand(options = {}) {
|
|
3736
|
+
if (options.foreground) {
|
|
3737
|
+
await startGatewayForegroundCommand();
|
|
3738
|
+
return;
|
|
3739
|
+
}
|
|
3740
|
+
const config = await loadConfig();
|
|
3741
|
+
const secrets = await loadSecrets();
|
|
3742
|
+
const result = startDetachedGateway({ config, secrets });
|
|
3743
|
+
console.log(result.message);
|
|
3744
|
+
if (result.pid) {
|
|
3745
|
+
console.log(`PID\uFF1A${result.pid}`);
|
|
3746
|
+
}
|
|
3747
|
+
console.log(`\u65E5\u5FD7\u6587\u4EF6\uFF1A${result.logFile}`);
|
|
3748
|
+
console.log("\u67E5\u770B\u65E5\u5FD7\uFF1Achattercatcher logs --follow --file gateway.log");
|
|
3749
|
+
console.log("\u505C\u6B62 Gateway\uFF1Achattercatcher gateway stop");
|
|
3750
|
+
}
|
|
3381
3751
|
gateway.command("status").description("\u67E5\u770B Gateway \u72B6\u6001").action(async () => {
|
|
3382
3752
|
const config = await loadConfig();
|
|
3383
3753
|
const secrets = await loadSecrets();
|
|
3384
3754
|
console.log(JSON.stringify(getGatewayStatus(config, secrets), null, 2));
|
|
3385
3755
|
});
|
|
3386
|
-
gateway.command("start").description("\u542F\u52A8\u98DE\u4E66\u957F\u8FDE\u63A5 Gateway \u548C\u672C\u5730 Web UI").action(startGatewayCommand);
|
|
3756
|
+
gateway.command("start").description("\u542F\u52A8\u98DE\u4E66\u957F\u8FDE\u63A5 Gateway \u548C\u672C\u5730 Web UI").option("--foreground", "\u5728\u5F53\u524D\u7EC8\u7AEF\u4EE5\u524D\u53F0\u6A21\u5F0F\u8FD0\u884C").action(startGatewayCommand);
|
|
3387
3757
|
gateway.command("stop").description("\u505C\u6B62 Gateway").action(() => {
|
|
3388
3758
|
console.log(stopGatewayProcess().message);
|
|
3389
3759
|
});
|
|
@@ -3419,7 +3789,7 @@ async function deleteDataCommand(targetType, targetId, options) {
|
|
|
3419
3789
|
if (result.skippedStoredFiles.length > 0) {
|
|
3420
3790
|
console.log(`\u8DF3\u8FC7\u975E\u6570\u636E\u76EE\u5F55\u6587\u4EF6\uFF1A${result.skippedStoredFiles.join("\uFF1B")}`);
|
|
3421
3791
|
}
|
|
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
|
|
3792
|
+
console.log("SQLite FTS \u5DF2\u540C\u6B65\u5220\u9664\uFF1B\u5982\u4F7F\u7528 LanceDB \u8BED\u4E49\u68C0\u7D22\uFF0C\u8BF7\u8FD0\u884C chattercatcher index rebuild\u3002");
|
|
3423
3793
|
} finally {
|
|
3424
3794
|
database.close();
|
|
3425
3795
|
}
|
|
@@ -3434,19 +3804,20 @@ index.command("status").description("\u67E5\u770B\u7D22\u5F15\u72B6\u6001").acti
|
|
|
3434
3804
|
const secrets = await loadSecrets();
|
|
3435
3805
|
const database = openDatabase(config);
|
|
3436
3806
|
const messages = new MessageRepository(database);
|
|
3437
|
-
const
|
|
3807
|
+
const { getLanceDbPath: getLanceDbPath2, LanceDbVectorStore: LanceDbVectorStore2 } = await Promise.resolve().then(() => (init_lancedb_store(), lancedb_store_exports));
|
|
3808
|
+
const vectorStore = await LanceDbVectorStore2.connectFromConfig(config);
|
|
3438
3809
|
const vectors = await vectorStore.count();
|
|
3439
3810
|
console.log(JSON.stringify(
|
|
3440
3811
|
{
|
|
3441
3812
|
database: getDatabasePath(config),
|
|
3442
|
-
vectorDatabase:
|
|
3813
|
+
vectorDatabase: getLanceDbPath2(config),
|
|
3443
3814
|
chats: messages.getChatCount(),
|
|
3444
3815
|
messages: messages.getMessageCount(),
|
|
3445
3816
|
vectors,
|
|
3446
3817
|
retrieval: {
|
|
3447
3818
|
keyword: "SQLite FTS5",
|
|
3448
|
-
vector: hasEmbeddingConfig(config, secrets) ? "
|
|
3449
|
-
hybrid: "\u542F\u7528\uFF1ASQLite FTS +
|
|
3819
|
+
vector: hasEmbeddingConfig(config, secrets) ? "LanceDB \u5DF2\u53EF\u7528\u4E8E\u8BED\u4E49\u68C0\u7D22" : "LanceDB \u5DF2\u63A5\u5165\uFF1B\u9700\u914D\u7F6E embedding \u540E\u542F\u7528\u8BED\u4E49\u68C0\u7D22",
|
|
3820
|
+
hybrid: "\u542F\u7528\uFF1ASQLite FTS + LanceDB Vector",
|
|
3450
3821
|
rag: "\u5F3A\u5236\u5148\u68C0\u7D22\u8BC1\u636E\u518D\u56DE\u7B54\uFF0C\u7981\u6B62\u5168\u91CF\u4E0A\u4E0B\u6587\u5806\u53E0"
|
|
3451
3822
|
}
|
|
3452
3823
|
},
|
|
@@ -3456,7 +3827,7 @@ index.command("status").description("\u67E5\u770B\u7D22\u5F15\u72B6\u6001").acti
|
|
|
3456
3827
|
vectorStore.close();
|
|
3457
3828
|
database.close();
|
|
3458
3829
|
});
|
|
3459
|
-
index.command("rebuild").description("\u91CD\u5EFA
|
|
3830
|
+
index.command("rebuild").description("\u91CD\u5EFA LanceDB \u5411\u91CF\u7D22\u5F15").option("--limit <number>", "\u6700\u591A\u7D22\u5F15\u7684 chunk \u6570", "10000").action(async (options) => {
|
|
3460
3831
|
const config = await loadConfig();
|
|
3461
3832
|
const secrets = await loadSecrets();
|
|
3462
3833
|
if (!hasEmbeddingConfig(config, secrets)) {
|
|
@@ -3464,7 +3835,8 @@ index.command("rebuild").description("\u91CD\u5EFA SQLite \u5411\u91CF\u7D22\u5F
|
|
|
3464
3835
|
return;
|
|
3465
3836
|
}
|
|
3466
3837
|
const database = openDatabase(config);
|
|
3467
|
-
const
|
|
3838
|
+
const { LanceDbVectorStore: LanceDbVectorStore2 } = await Promise.resolve().then(() => (init_lancedb_store(), lancedb_store_exports));
|
|
3839
|
+
const vectorStore = await LanceDbVectorStore2.connectFromConfig(config);
|
|
3468
3840
|
try {
|
|
3469
3841
|
const stats = await indexMessageChunks({
|
|
3470
3842
|
messages: new MessageRepository(database),
|
|
@@ -3479,7 +3851,7 @@ index.command("rebuild").description("\u91CD\u5EFA SQLite \u5411\u91CF\u7D22\u5F
|
|
|
3479
3851
|
}
|
|
3480
3852
|
});
|
|
3481
3853
|
var processCommand = program.command("process").description("\u7ACB\u5373\u5904\u7406\u540E\u53F0\u4EFB\u52A1");
|
|
3482
|
-
processCommand.command("messages").description("\u7ACB\u5373\u5904\u7406\u6D88\u606F\u7D22\u5F15\u4EFB\u52A1\uFF0C\u628A\u6D88\u606F chunks \u5199\u5165
|
|
3854
|
+
processCommand.command("messages").description("\u7ACB\u5373\u5904\u7406\u6D88\u606F\u7D22\u5F15\u4EFB\u52A1\uFF0C\u628A\u6D88\u606F chunks \u5199\u5165 LanceDB \u5411\u91CF\u7D22\u5F15").option("--limit <number>", "\u6700\u591A\u5904\u7406\u7684 chunk \u6570", "10000").action(async (options) => {
|
|
3483
3855
|
const config = await loadConfig();
|
|
3484
3856
|
const secrets = await loadSecrets();
|
|
3485
3857
|
const database = openDatabase(config);
|
|
@@ -3513,7 +3885,7 @@ files.command("add").description("\u628A\u672C\u5730\u6587\u4EF6\u89E3\u6790\u30
|
|
|
3513
3885
|
`\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}`
|
|
3514
3886
|
);
|
|
3515
3887
|
}
|
|
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
|
|
3888
|
+
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 LanceDB \u5411\u91CF\u7D22\u5F15\u3002");
|
|
3517
3889
|
} finally {
|
|
3518
3890
|
database.close();
|
|
3519
3891
|
}
|
|
@@ -3642,7 +4014,7 @@ program.command("restore").description("\u4ECE ChatterCatcher \u5BFC\u51FA\u6587
|
|
|
3642
4014
|
console.log(`\u6062\u590D\u5B8C\u6210\uFF1A${result.inputPath}`);
|
|
3643
4015
|
console.log(`\u6A21\u5F0F\uFF1A${result.mode === "replace" ? "\u66FF\u6362" : "\u5408\u5E76"}`);
|
|
3644
4016
|
console.log(`\u5305\u542B\uFF1A\u7FA4\u804A=${result.chats}\uFF0C\u6D88\u606F=${result.messages}\uFF0Cchunks=${result.chunks}\uFF0C\u6587\u4EF6\u4EFB\u52A1=${result.fileJobs}`);
|
|
3645
|
-
console.log("SQLite FTS \u5DF2\u91CD\u5EFA\uFF1B\u5982\u4F7F\u7528\u8BED\u4E49\u68C0\u7D22\uFF0C\u8BF7\u8FD0\u884C chattercatcher index rebuild
|
|
4017
|
+
console.log("SQLite FTS \u5DF2\u91CD\u5EFA\uFF1B\u5982\u4F7F\u7528 LanceDB \u8BED\u4E49\u68C0\u7D22\uFF0C\u8BF7\u8FD0\u884C chattercatcher index rebuild\u3002");
|
|
3646
4018
|
} finally {
|
|
3647
4019
|
database.close();
|
|
3648
4020
|
}
|
|
@@ -3672,7 +4044,7 @@ dev.command("ingest-feishu-event").description("\u4ECE JSON \u6587\u4EF6\u6A21\u
|
|
|
3672
4044
|
const config = await loadConfig();
|
|
3673
4045
|
const database = openDatabase(config);
|
|
3674
4046
|
try {
|
|
3675
|
-
const raw = await
|
|
4047
|
+
const raw = await fs14.readFile(options.file, "utf8");
|
|
3676
4048
|
const payload = JSON.parse(raw);
|
|
3677
4049
|
const result = new GatewayIngestor(database).ingestFeishuEvent(payload);
|
|
3678
4050
|
if (!result.accepted) {
|