chattercatcher 0.1.15 → 0.1.16
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 +79 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +15 -3
- package/dist/index.js +76 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import fs13 from "fs/promises";
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "chattercatcher",
|
|
11
|
-
version: "0.1.
|
|
11
|
+
version: "0.1.16",
|
|
12
12
|
description: "\u672C\u5730\u4F18\u5148\u7684\u98DE\u4E66/Lark \u5BB6\u5EAD\u7FA4\u77E5\u8BC6\u5E93\u673A\u5668\u4EBA",
|
|
13
13
|
type: "module",
|
|
14
14
|
main: "dist/index.js",
|
|
@@ -36,6 +36,7 @@ var package_default = {
|
|
|
36
36
|
},
|
|
37
37
|
scripts: {
|
|
38
38
|
build: "tsup",
|
|
39
|
+
prepack: "npm run build",
|
|
39
40
|
dev: "tsx src/cli.ts",
|
|
40
41
|
lint: "tsc --noEmit",
|
|
41
42
|
typecheck: "tsc --noEmit",
|
|
@@ -52,7 +53,7 @@ var package_default = {
|
|
|
52
53
|
license: "MIT",
|
|
53
54
|
dependencies: {
|
|
54
55
|
"@inquirer/prompts": "^8.4.2",
|
|
55
|
-
"@larksuiteoapi/node-sdk": "^1.62.
|
|
56
|
+
"@larksuiteoapi/node-sdk": "^1.62.1",
|
|
56
57
|
"better-sqlite3": "^12.9.0",
|
|
57
58
|
commander: "^14.0.3",
|
|
58
59
|
fastify: "^5.8.5",
|
|
@@ -114,7 +115,7 @@ var appConfigSchema = z.object({
|
|
|
114
115
|
episodes: z.object({
|
|
115
116
|
windowMinutes: z.number().int().positive().default(10),
|
|
116
117
|
quietMinutes: z.number().int().positive().default(2)
|
|
117
|
-
})
|
|
118
|
+
}).default({ windowMinutes: 10, quietMinutes: 2 })
|
|
118
119
|
});
|
|
119
120
|
var appSecretsSchema = z.object({
|
|
120
121
|
feishu: z.object({
|
|
@@ -1443,6 +1444,29 @@ var EpisodeRepository = class {
|
|
|
1443
1444
|
messageIds: window.messages.map((message) => message.id)
|
|
1444
1445
|
};
|
|
1445
1446
|
}
|
|
1447
|
+
getEpisodeCount() {
|
|
1448
|
+
const row = this.database.prepare("SELECT count(*) AS count FROM memory_episodes").get();
|
|
1449
|
+
return row.count;
|
|
1450
|
+
}
|
|
1451
|
+
listRecentEpisodes(limit = 20) {
|
|
1452
|
+
return this.database.prepare(
|
|
1453
|
+
`
|
|
1454
|
+
SELECT
|
|
1455
|
+
e.id,
|
|
1456
|
+
e.chat_id AS chatId,
|
|
1457
|
+
c.name AS chatName,
|
|
1458
|
+
e.summary,
|
|
1459
|
+
e.message_count AS messageCount,
|
|
1460
|
+
e.started_at AS startedAt,
|
|
1461
|
+
e.ended_at AS endedAt,
|
|
1462
|
+
e.created_at AS createdAt
|
|
1463
|
+
FROM memory_episodes e
|
|
1464
|
+
JOIN chats c ON c.id = e.chat_id
|
|
1465
|
+
ORDER BY e.ended_at DESC
|
|
1466
|
+
LIMIT ?
|
|
1467
|
+
`
|
|
1468
|
+
).all(limit);
|
|
1469
|
+
}
|
|
1446
1470
|
searchEpisodes(query, limit = 8) {
|
|
1447
1471
|
const ftsQuery = escapeFtsQuery2(query);
|
|
1448
1472
|
return this.database.prepare(
|
|
@@ -2668,6 +2692,13 @@ function assertFeishuConfig(config, secrets) {
|
|
|
2668
2692
|
throw new Error("\u98DE\u4E66\u914D\u7F6E\u4E0D\u5B8C\u6574\u3002\u8BF7\u5148\u8FD0\u884C chattercatcher setup \u6216 chattercatcher settings\u3002");
|
|
2669
2693
|
}
|
|
2670
2694
|
}
|
|
2695
|
+
function formatGatewayStartError(error) {
|
|
2696
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2697
|
+
if (message.includes("PingInterval") || message.includes("system busy") || message.includes("1000040345")) {
|
|
2698
|
+
return new Error(`\u98DE\u4E66\u957F\u8FDE\u63A5\u542F\u52A8\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 App ID / App Secret \u662F\u5426\u6B63\u786E\uFF1B\u539F\u59CB\u9519\u8BEF\uFF1A${message}`);
|
|
2699
|
+
}
|
|
2700
|
+
return error instanceof Error ? error : new Error(message);
|
|
2701
|
+
}
|
|
2671
2702
|
function createFeishuEventDispatcher(options) {
|
|
2672
2703
|
const answeredMessageIds = /* @__PURE__ */ new Set();
|
|
2673
2704
|
return new lark2.EventDispatcher({}).register({
|
|
@@ -2772,7 +2803,11 @@ function createFeishuGateway(options) {
|
|
|
2772
2803
|
});
|
|
2773
2804
|
return {
|
|
2774
2805
|
async start() {
|
|
2775
|
-
|
|
2806
|
+
try {
|
|
2807
|
+
await wsClient.start({ eventDispatcher });
|
|
2808
|
+
} catch (error) {
|
|
2809
|
+
throw formatGatewayStartError(error);
|
|
2810
|
+
}
|
|
2776
2811
|
},
|
|
2777
2812
|
stop() {
|
|
2778
2813
|
wsClient.close({ force: true });
|
|
@@ -3626,6 +3661,10 @@ function buildHtml() {
|
|
|
3626
3661
|
<h2>\u6700\u8FD1\u6D88\u606F</h2>
|
|
3627
3662
|
<div id="messages" class="empty">\u6B63\u5728\u8BFB\u53D6...</div>
|
|
3628
3663
|
</section>
|
|
3664
|
+
<section>
|
|
3665
|
+
<h2>\u4F1A\u8BDD\u8BB0\u5FC6</h2>
|
|
3666
|
+
<div id="episodes" class="empty">\u6B63\u5728\u8BFB\u53D6...</div>
|
|
3667
|
+
</section>
|
|
3629
3668
|
</div>
|
|
3630
3669
|
<aside>
|
|
3631
3670
|
<section>
|
|
@@ -3652,6 +3691,7 @@ function buildHtml() {
|
|
|
3652
3691
|
<script>
|
|
3653
3692
|
const metrics = document.querySelector("#metrics");
|
|
3654
3693
|
const messages = document.querySelector("#messages");
|
|
3694
|
+
const episodes = document.querySelector("#episodes");
|
|
3655
3695
|
const chats = document.querySelector("#chats");
|
|
3656
3696
|
const files = document.querySelector("#files");
|
|
3657
3697
|
const fileJobs = document.querySelector("#file-jobs");
|
|
@@ -3715,6 +3755,7 @@ function buildHtml() {
|
|
|
3715
3755
|
["Gateway", formatGatewayValue(status.gateway), formatGatewayNote(status.gateway), gatewayClass],
|
|
3716
3756
|
["\u7FA4\u804A", status.data.chats, "\u672C\u5730\u7FA4\u804A\u6570", ""],
|
|
3717
3757
|
["\u6D88\u606F", status.data.messages, "\u5DF2\u5165\u5E93\u6D88\u606F", ""],
|
|
3758
|
+
["\u4F1A\u8BDD\u8BB0\u5FC6", status.data.episodes, "\u5DF2\u751F\u6210\u6458\u8981", ""],
|
|
3718
3759
|
["\u6587\u4EF6", status.data.files, "\u6587\u4EF6\u77E5\u8BC6\u6E90", ""],
|
|
3719
3760
|
].map(([label, value, note, extra]) => \`
|
|
3720
3761
|
<div class="metric">
|
|
@@ -3748,6 +3789,29 @@ function buildHtml() {
|
|
|
3748
3789
|
\`;
|
|
3749
3790
|
}
|
|
3750
3791
|
|
|
3792
|
+
function renderEpisodes(items) {
|
|
3793
|
+
if (items.length === 0) {
|
|
3794
|
+
episodes.className = "empty";
|
|
3795
|
+
episodes.textContent = "\u8FD8\u6CA1\u6709\u4F1A\u8BDD\u8BB0\u5FC6\u3002\u9ED8\u8BA4\u5728 10 \u5206\u949F\u7A97\u53E3\u9759\u9ED8 2 \u5206\u949F\u540E\u751F\u6210\uFF0C\u4E5F\u53EF\u4EE5\u8FD0\u884C chattercatcher process episodes \u624B\u52A8\u89E6\u53D1\u3002";
|
|
3796
|
+
return;
|
|
3797
|
+
}
|
|
3798
|
+
episodes.className = "";
|
|
3799
|
+
episodes.innerHTML = \`
|
|
3800
|
+
<div class="message-list">
|
|
3801
|
+
\${items.map((item) => \`
|
|
3802
|
+
<article class="message-item">
|
|
3803
|
+
<div class="message-meta">
|
|
3804
|
+
<span>\${escapeHtml(formatDateTime(item.startedAt))} - \${escapeHtml(formatDateTime(item.endedAt))}</span>
|
|
3805
|
+
<span>\${escapeHtml(displayChatName(item.chatName, "feishu"))}</span>
|
|
3806
|
+
<span>\${escapeHtml(item.messageCount)} \u6761\u6D88\u606F</span>
|
|
3807
|
+
</div>
|
|
3808
|
+
<div class="message-body">\${escapeHtml(item.summary)}</div>
|
|
3809
|
+
</article>
|
|
3810
|
+
\`).join("")}
|
|
3811
|
+
</div>
|
|
3812
|
+
\`;
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3751
3815
|
function renderChats(items) {
|
|
3752
3816
|
if (items.length === 0) {
|
|
3753
3817
|
chats.className = "empty";
|
|
@@ -3823,15 +3887,17 @@ function buildHtml() {
|
|
|
3823
3887
|
}
|
|
3824
3888
|
|
|
3825
3889
|
async function load() {
|
|
3826
|
-
const [status, recent, chatList, fileList, jobList] = await Promise.all([
|
|
3890
|
+
const [status, recent, episodeList, chatList, fileList, jobList] = await Promise.all([
|
|
3827
3891
|
fetch("/api/status").then((response) => response.json()),
|
|
3828
3892
|
fetch("/api/messages/recent?limit=20").then((response) => response.json()),
|
|
3893
|
+
fetch("/api/episodes?limit=10").then((response) => response.json()),
|
|
3829
3894
|
fetch("/api/chats").then((response) => response.json()),
|
|
3830
3895
|
fetch("/api/files").then((response) => response.json()),
|
|
3831
3896
|
fetch("/api/file-jobs").then((response) => response.json()),
|
|
3832
3897
|
]);
|
|
3833
3898
|
renderMetrics(status);
|
|
3834
3899
|
renderMessages(recent.items);
|
|
3900
|
+
renderEpisodes(episodeList.items);
|
|
3835
3901
|
renderChats(chatList.items);
|
|
3836
3902
|
renderFiles(fileList.items);
|
|
3837
3903
|
renderFileJobs(jobList.items);
|
|
@@ -3880,6 +3946,7 @@ function createWebApp(config) {
|
|
|
3880
3946
|
const app = Fastify({ logger: false });
|
|
3881
3947
|
const database = openDatabase(config);
|
|
3882
3948
|
const messages = new MessageRepository(database);
|
|
3949
|
+
const episodes = new EpisodeRepository(database);
|
|
3883
3950
|
const fileJobs = new FileJobRepository(database);
|
|
3884
3951
|
app.addHook("onClose", async () => {
|
|
3885
3952
|
database.close();
|
|
@@ -3890,6 +3957,7 @@ function createWebApp(config) {
|
|
|
3890
3957
|
data: {
|
|
3891
3958
|
chats: messages.getChatCount(),
|
|
3892
3959
|
messages: messages.getMessageCount(),
|
|
3960
|
+
episodes: episodes.getEpisodeCount(),
|
|
3893
3961
|
files: messages.listFiles(1e3).length
|
|
3894
3962
|
},
|
|
3895
3963
|
rag: {
|
|
@@ -3925,6 +3993,12 @@ function createWebApp(config) {
|
|
|
3925
3993
|
items: messages.listRecentMessages(limit)
|
|
3926
3994
|
};
|
|
3927
3995
|
});
|
|
3996
|
+
app.get("/api/episodes", async (request) => {
|
|
3997
|
+
const limit = parseLimit(request.query.limit, 20, 100);
|
|
3998
|
+
return {
|
|
3999
|
+
items: episodes.listRecentEpisodes(limit)
|
|
4000
|
+
};
|
|
4001
|
+
});
|
|
3928
4002
|
app.post("/api/process/messages", async (_request, reply) => {
|
|
3929
4003
|
try {
|
|
3930
4004
|
return await processMessagesNow({
|