chattercatcher 0.1.26 → 0.1.27
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 +145 -42
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +132 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { input, password, select, confirm, number } from "@inquirer/prompts";
|
|
5
5
|
import { Command } from "commander";
|
|
6
|
-
import
|
|
6
|
+
import fs15 from "fs/promises";
|
|
7
7
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "chattercatcher",
|
|
11
|
-
version: "0.1.
|
|
11
|
+
version: "0.1.27",
|
|
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",
|
|
@@ -548,12 +548,17 @@ function migrateDatabase(database) {
|
|
|
548
548
|
next_run_at TEXT NOT NULL,
|
|
549
549
|
last_error TEXT,
|
|
550
550
|
created_at TEXT NOT NULL,
|
|
551
|
-
updated_at TEXT NOT NULL
|
|
551
|
+
updated_at TEXT NOT NULL,
|
|
552
|
+
image_file_name TEXT
|
|
552
553
|
);
|
|
553
554
|
|
|
554
555
|
CREATE INDEX IF NOT EXISTS cron_jobs_chat_status_idx ON cron_jobs(chat_id, status, updated_at);
|
|
555
556
|
CREATE INDEX IF NOT EXISTS cron_jobs_due_idx ON cron_jobs(status, next_run_at);
|
|
556
557
|
`);
|
|
558
|
+
const cronJobColumns = database.prepare("PRAGMA table_info(cron_jobs)").all();
|
|
559
|
+
if (!cronJobColumns.some((column) => column.name === "image_file_name")) {
|
|
560
|
+
database.prepare("ALTER TABLE cron_jobs ADD COLUMN image_file_name TEXT").run();
|
|
561
|
+
}
|
|
557
562
|
}
|
|
558
563
|
|
|
559
564
|
// src/doctor/checks.ts
|
|
@@ -1304,6 +1309,9 @@ var MessageRepository = class {
|
|
|
1304
1309
|
throw new Error("\u539F\u59CB\u56FE\u7247\u6D88\u606F\u4E0D\u5B58\u5728\u3002");
|
|
1305
1310
|
}
|
|
1306
1311
|
const derivedPlatformMessageId = `${source.platformMessageId}:image-summary:${input2.imageKey}`;
|
|
1312
|
+
const imageFileName = input2.imageFileName?.trim();
|
|
1313
|
+
const summaryText = imageFileName ? `[\u56FE\u7247\u8F6C\u8FF0] \u6587\u4EF6\u540D\uFF1A${imageFileName}
|
|
1314
|
+
${input2.summary.trim()}` : `[\u56FE\u7247\u8F6C\u8FF0] ${input2.summary.trim()}`;
|
|
1307
1315
|
return this.ingest({
|
|
1308
1316
|
platform: source.platform,
|
|
1309
1317
|
platformChatId: source.platformChatId,
|
|
@@ -1312,12 +1320,13 @@ var MessageRepository = class {
|
|
|
1312
1320
|
senderId: source.senderId,
|
|
1313
1321
|
senderName: source.senderName,
|
|
1314
1322
|
messageType: "image_summary",
|
|
1315
|
-
text:
|
|
1323
|
+
text: summaryText,
|
|
1316
1324
|
sentAt: source.sentAt,
|
|
1317
1325
|
rawPayload: {
|
|
1318
1326
|
derivedFromMessageId: input2.sourceMessageId,
|
|
1319
1327
|
sourceAttachmentKind: "image",
|
|
1320
1328
|
sourceResourceKey: input2.imageKey,
|
|
1329
|
+
...imageFileName ? { imageFileName } : {},
|
|
1321
1330
|
multimodalModel: input2.multimodalModel,
|
|
1322
1331
|
isMeaningful: true,
|
|
1323
1332
|
...input2.reason?.trim() ? { reason: input2.reason.trim() } : {},
|
|
@@ -2618,7 +2627,7 @@ async function summarizeEpisodeWindow(window, model, now) {
|
|
|
2618
2627
|
const summary = await model.complete([
|
|
2619
2628
|
{
|
|
2620
2629
|
role: "system",
|
|
2621
|
-
content: "\u4F60\u662F ChatterCatcher \u7684\u4F1A\u8BDD\u8BB0\u5FC6\u6574\u7406\u6A21\u5757\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u628A\u788E\u7247\u5316\u95F2\u804A\u6574\u7406\u6210\u53EF\u68C0\u7D22\u4E8B\u5B9E\uFF0C\u8865\u5168\u77ED\u6D88\u606F\u3001\u4EE3\u8BCD\u3001\u7F29\u5199\u4E0E\u4E0A\u4E0B\u6587\u4E4B\u95F4\u7684\u5173\u7CFB\u3002\u53EA\u603B\u7ED3\u660E\u786E\u4E8B\u5B9E\uFF0C\u4E0D\u8981\u7F16\u9020\u3002\u4FDD\u7559\u91CD\u8981\u6570\u5B57\u3001\u65E5\u671F\u3001\u94FE\u63A5\u548C\u4EE3\u7801\uFF1B\u5982\u679C\u5185\u5BB9\u50CF\u5BC6\u7801\u3001API key\u3001token \u6216\u5BC6\u94A5\uFF0C\u53EA\u63CF\u8FF0\u5176\u4E0A\u4E0B\u6587\u5173\u7CFB\uFF0C\u4E0D\u8981\u5728\u6458\u8981\u4E2D\u590D\u5199\u539F\u6587\u3002\u6D88\u606F\u91CC\u7684\u201C\u4ECA\u5929\u201D\u201C\u660E\u5929\u201D\u201C\u6628\u665A\u201D\u201C\u4E0B\u5468\u4E09\u201D\u7B49\u76F8\u5BF9\u65F6\u95F4\u8868\u8FF0\uFF0C\u8BF7\u57FA\u4E8E\u6BCF\u6761\u6D88\u606F\u524D\u7684\u53D1\u9001\u65F6\u95F4\u6233\u63A8\u5BFC\u4E3A\u5177\u4F53\u65E5\u671F\u5199\u5165\u6458\u8981\u3002\u4F8B\u5982 [2026-05-05T20:00:00.000Z] \u5988\u5988\u8BF4\u201C\u660E\u5929\u8981\u7528\u4E1D\u4E1D\u9732\u201D\uFF0C\u6458\u8981\u5E94\u5199\u4E3A\u201C2026-05-06 \u8981\u7528\u4E1D\u4E1D\u9732\u201D\u3002"
|
|
2630
|
+
content: "\u4F60\u662F ChatterCatcher \u7684\u4F1A\u8BDD\u8BB0\u5FC6\u6574\u7406\u6A21\u5757\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u628A\u788E\u7247\u5316\u95F2\u804A\u6574\u7406\u6210\u53EF\u68C0\u7D22\u4E8B\u5B9E\uFF0C\u8865\u5168\u77ED\u6D88\u606F\u3001\u4EE3\u8BCD\u3001\u7F29\u5199\u4E0E\u4E0A\u4E0B\u6587\u4E4B\u95F4\u7684\u5173\u7CFB\u3002\u53EA\u603B\u7ED3\u660E\u786E\u4E8B\u5B9E\uFF0C\u4E0D\u8981\u7F16\u9020\u3002\u4FDD\u7559\u91CD\u8981\u6570\u5B57\u3001\u65E5\u671F\u3001\u94FE\u63A5\u3001\u6587\u4EF6\u540D\u548C\u4EE3\u7801\uFF1B\u5982\u679C\u56FE\u7247\u8F6C\u8FF0\u91CC\u51FA\u73B0\u6587\u4EF6\u540D\uFF0C\u5FC5\u987B\u5728\u6458\u8981\u4E2D\u539F\u6837\u4FDD\u7559\u8BE5\u6587\u4EF6\u540D\uFF0C\u65B9\u4FBF\u4E4B\u540E\u6309\u6587\u4EF6\u540D\u627E\u56DE\u56FE\u7247\u3002\u5982\u679C\u5185\u5BB9\u50CF\u5BC6\u7801\u3001API key\u3001token \u6216\u5BC6\u94A5\uFF0C\u53EA\u63CF\u8FF0\u5176\u4E0A\u4E0B\u6587\u5173\u7CFB\uFF0C\u4E0D\u8981\u5728\u6458\u8981\u4E2D\u590D\u5199\u539F\u6587\u3002\u6D88\u606F\u91CC\u7684\u201C\u4ECA\u5929\u201D\u201C\u660E\u5929\u201D\u201C\u6628\u665A\u201D\u201C\u4E0B\u5468\u4E09\u201D\u7B49\u76F8\u5BF9\u65F6\u95F4\u8868\u8FF0\uFF0C\u8BF7\u57FA\u4E8E\u6BCF\u6761\u6D88\u606F\u524D\u7684\u53D1\u9001\u65F6\u95F4\u6233\u63A8\u5BFC\u4E3A\u5177\u4F53\u65E5\u671F\u5199\u5165\u6458\u8981\u3002\u4F8B\u5982 [2026-05-05T20:00:00.000Z] \u5988\u5988\u8BF4\u201C\u660E\u5929\u8981\u7528\u4E1D\u4E1D\u9732\u201D\uFF0C\u6458\u8981\u5E94\u5199\u4E3A\u201C2026-05-06 \u8981\u7528\u4E1D\u4E1D\u9732\u201D\u3002"
|
|
2622
2631
|
},
|
|
2623
2632
|
{
|
|
2624
2633
|
role: "user",
|
|
@@ -2723,6 +2732,7 @@ async function ensureFeishuBotOpenId(config, secrets, options = {}) {
|
|
|
2723
2732
|
|
|
2724
2733
|
// src/feishu/gateway.ts
|
|
2725
2734
|
import * as lark2 from "@larksuiteoapi/node-sdk";
|
|
2735
|
+
import path12 from "path";
|
|
2726
2736
|
|
|
2727
2737
|
// src/cron/jobs.ts
|
|
2728
2738
|
import crypto4 from "crypto";
|
|
@@ -2827,6 +2837,7 @@ var CronJobRepository = class {
|
|
|
2827
2837
|
create(input2) {
|
|
2828
2838
|
const schedule = input2.schedule.trim();
|
|
2829
2839
|
const prompt = input2.prompt.trim();
|
|
2840
|
+
const imageFileName = input2.imageFileName?.trim();
|
|
2830
2841
|
if (!isValidCronSchedule(schedule)) {
|
|
2831
2842
|
throw new Error("cron \u8868\u8FBE\u5F0F\u65E0\u6548\u3002");
|
|
2832
2843
|
}
|
|
@@ -2844,6 +2855,7 @@ var CronJobRepository = class {
|
|
|
2844
2855
|
createdByOpenId: input2.createdByOpenId,
|
|
2845
2856
|
schedule,
|
|
2846
2857
|
prompt,
|
|
2858
|
+
...imageFileName ? { imageFileName } : {},
|
|
2847
2859
|
status: "active",
|
|
2848
2860
|
nextRunAt: nextRunAt.toISOString(),
|
|
2849
2861
|
createdAt: now.toISOString(),
|
|
@@ -2852,15 +2864,18 @@ var CronJobRepository = class {
|
|
|
2852
2864
|
this.database.prepare(
|
|
2853
2865
|
`
|
|
2854
2866
|
INSERT INTO cron_jobs (
|
|
2855
|
-
id, chat_id, created_by_open_id, schedule, prompt, status,
|
|
2867
|
+
id, chat_id, created_by_open_id, schedule, prompt, image_file_name, status,
|
|
2856
2868
|
last_run_at, next_run_at, last_error, created_at, updated_at
|
|
2857
2869
|
)
|
|
2858
2870
|
VALUES (
|
|
2859
|
-
@id, @chatId, @createdByOpenId, @schedule, @prompt, @status,
|
|
2871
|
+
@id, @chatId, @createdByOpenId, @schedule, @prompt, @imageFileName, @status,
|
|
2860
2872
|
NULL, @nextRunAt, NULL, @createdAt, @updatedAt
|
|
2861
2873
|
)
|
|
2862
2874
|
`
|
|
2863
|
-
).run(
|
|
2875
|
+
).run({
|
|
2876
|
+
...record,
|
|
2877
|
+
imageFileName: record.imageFileName ?? null
|
|
2878
|
+
});
|
|
2864
2879
|
return record;
|
|
2865
2880
|
}
|
|
2866
2881
|
get(id) {
|
|
@@ -2885,6 +2900,7 @@ var CronJobRepository = class {
|
|
|
2885
2900
|
created_by_open_id AS createdByOpenId,
|
|
2886
2901
|
schedule,
|
|
2887
2902
|
prompt,
|
|
2903
|
+
image_file_name AS imageFileName,
|
|
2888
2904
|
status,
|
|
2889
2905
|
last_run_at AS lastRunAt,
|
|
2890
2906
|
next_run_at AS nextRunAt,
|
|
@@ -2903,6 +2919,7 @@ var CronJobRepository = class {
|
|
|
2903
2919
|
createdByOpenId: row.createdByOpenId ?? void 0,
|
|
2904
2920
|
schedule: row.schedule,
|
|
2905
2921
|
prompt: row.prompt,
|
|
2922
|
+
imageFileName: row.imageFileName ?? void 0,
|
|
2906
2923
|
status: row.status,
|
|
2907
2924
|
lastRunAt: row.lastRunAt ?? void 0,
|
|
2908
2925
|
nextRunAt: row.nextRunAt,
|
|
@@ -2976,6 +2993,7 @@ var CronJobRepository = class {
|
|
|
2976
2993
|
created_by_open_id AS createdByOpenId,
|
|
2977
2994
|
schedule,
|
|
2978
2995
|
prompt,
|
|
2996
|
+
image_file_name AS imageFileName,
|
|
2979
2997
|
status,
|
|
2980
2998
|
last_run_at AS lastRunAt,
|
|
2981
2999
|
next_run_at AS nextRunAt,
|
|
@@ -2994,6 +3012,7 @@ var CronJobRepository = class {
|
|
|
2994
3012
|
createdByOpenId: row.createdByOpenId ?? void 0,
|
|
2995
3013
|
schedule: row.schedule,
|
|
2996
3014
|
prompt: row.prompt,
|
|
3015
|
+
imageFileName: row.imageFileName ?? void 0,
|
|
2997
3016
|
status: row.status,
|
|
2998
3017
|
lastRunAt: row.lastRunAt ?? void 0,
|
|
2999
3018
|
nextRunAt: row.nextRunAt,
|
|
@@ -3098,6 +3117,12 @@ function createCronJobScheduler(options) {
|
|
|
3098
3117
|
try {
|
|
3099
3118
|
const text = await options.generateMessage(job, startedAt);
|
|
3100
3119
|
await options.sendTextToChat(job.chatId, text);
|
|
3120
|
+
if (job.imageFileName) {
|
|
3121
|
+
if (!options.sendImageToChat) {
|
|
3122
|
+
throw new Error("\u5F53\u524D\u5B9A\u65F6\u4EFB\u52A1\u8FD0\u884C\u73AF\u5883\u4E0D\u652F\u6301\u53D1\u9001\u56FE\u7247\u3002");
|
|
3123
|
+
}
|
|
3124
|
+
await options.sendImageToChat(job.chatId, job.imageFileName);
|
|
3125
|
+
}
|
|
3101
3126
|
options.repository.markSuccess(job.id, startedAt);
|
|
3102
3127
|
} catch (error) {
|
|
3103
3128
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3514,6 +3539,7 @@ var ImageMultimodalTaskRepository = class {
|
|
|
3514
3539
|
};
|
|
3515
3540
|
|
|
3516
3541
|
// src/multimodal/worker.ts
|
|
3542
|
+
import path11 from "path";
|
|
3517
3543
|
var ImageMultimodalWorker = class {
|
|
3518
3544
|
constructor(options) {
|
|
3519
3545
|
this.options = options;
|
|
@@ -3540,9 +3566,11 @@ var ImageMultimodalWorker = class {
|
|
|
3540
3566
|
throw error;
|
|
3541
3567
|
}
|
|
3542
3568
|
try {
|
|
3569
|
+
const imageFileName = path11.basename(running.storedPath);
|
|
3543
3570
|
const described = await this.options.model.describeImage({
|
|
3544
3571
|
imagePath: running.storedPath,
|
|
3545
|
-
mimeType: running.mimeType
|
|
3572
|
+
mimeType: running.mimeType,
|
|
3573
|
+
context: `\u56FE\u7247\u6587\u4EF6\u540D\uFF1A${imageFileName}`
|
|
3546
3574
|
});
|
|
3547
3575
|
if (!described.isMeaningful) {
|
|
3548
3576
|
this.options.tasks.markSkipped(running.id, described.reason || "\u591A\u6A21\u6001\u6A21\u578B\u5224\u5B9A\u56FE\u7247\u65E0\u610F\u4E49\u3002");
|
|
@@ -3552,6 +3580,7 @@ var ImageMultimodalWorker = class {
|
|
|
3552
3580
|
const derivedMessageId = this.options.messages.createImageSummaryMessage({
|
|
3553
3581
|
sourceMessageId: running.sourceMessageId,
|
|
3554
3582
|
imageKey: running.imageKey,
|
|
3583
|
+
imageFileName,
|
|
3555
3584
|
summary: described.summary,
|
|
3556
3585
|
reason: described.reason,
|
|
3557
3586
|
multimodalModel: this.options.multimodalModelName,
|
|
@@ -3585,6 +3614,17 @@ function readString(input2, key) {
|
|
|
3585
3614
|
}
|
|
3586
3615
|
return value.trim();
|
|
3587
3616
|
}
|
|
3617
|
+
function readOptionalString(input2, key) {
|
|
3618
|
+
const value = typeof input2 === "object" && input2 !== null && key in input2 ? input2[key] : void 0;
|
|
3619
|
+
if (value === void 0 || value === null) {
|
|
3620
|
+
return void 0;
|
|
3621
|
+
}
|
|
3622
|
+
if (typeof value !== "string") {
|
|
3623
|
+
throw new Error(`${key} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u3002`);
|
|
3624
|
+
}
|
|
3625
|
+
const trimmed = value.trim();
|
|
3626
|
+
return trimmed || void 0;
|
|
3627
|
+
}
|
|
3588
3628
|
function createCronJobTools(input2) {
|
|
3589
3629
|
return [
|
|
3590
3630
|
{
|
|
@@ -3600,6 +3640,10 @@ function createCronJobTools(input2) {
|
|
|
3600
3640
|
prompt: {
|
|
3601
3641
|
type: "string",
|
|
3602
3642
|
description: "Prompt used later to generate the scheduled message."
|
|
3643
|
+
},
|
|
3644
|
+
imageFileName: {
|
|
3645
|
+
type: "string",
|
|
3646
|
+
description: "Optional image filename already stored from the current chat, for example om_xxx-image.jpg."
|
|
3603
3647
|
}
|
|
3604
3648
|
},
|
|
3605
3649
|
required: ["schedule", "prompt"],
|
|
@@ -3610,7 +3654,8 @@ function createCronJobTools(input2) {
|
|
|
3610
3654
|
chatId: input2.chatId,
|
|
3611
3655
|
createdByOpenId: input2.createdByOpenId,
|
|
3612
3656
|
schedule: readString(rawInput, "schedule"),
|
|
3613
|
-
prompt: readString(rawInput, "prompt")
|
|
3657
|
+
prompt: readString(rawInput, "prompt"),
|
|
3658
|
+
imageFileName: readOptionalString(rawInput, "imageFileName")
|
|
3614
3659
|
});
|
|
3615
3660
|
return JSON.stringify({ ok: true, job });
|
|
3616
3661
|
}
|
|
@@ -3773,7 +3818,7 @@ function stripMentions(text, mentions) {
|
|
|
3773
3818
|
}
|
|
3774
3819
|
return result.replace(/@/g, " ").replace(/\s+/g, " ").trim();
|
|
3775
3820
|
}
|
|
3776
|
-
var FEISHU_TOOL_SYSTEM_PROMPT = `\u4F60\u662F\u98DE\u4E66\u7FA4\u804A\u52A9\u624B\u3002\u4F60\u53EF\u4EE5\u5148\u641C\u7D22\u672C\u5730\u77E5\u8BC6\u6765\u56DE\u7B54\u95EE\u9898\uFF1B\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u521B\u5EFA\u3001\u67E5\u770B\u6216\u5220\u9664\u7FA4\u6D88\u606F\u5B9A\u65F6\u4EFB\u52A1\u65F6\uFF0C\u4E5F\u53EF\u4EE5\u8C03\u7528\u5B9A\u65F6\u4EFB\u52A1\u5DE5\u5177\u3002\u5B9A\u65F6\u4EFB\u52A1\u5DE5\u5177\u53EA\u7BA1\u7406\u5F53\u524D\u7FA4\u804A\uFF0C\u4E0D\u80FD\u8DE8\u7FA4\u64CD\u4F5C\u3002\u82E5\u7528\u6237\u7528\u81EA\u7136\u8BED\u8A00\u63CF\u8FF0\u65F6\u95F4\uFF0C\u4F60\u9700\u8981\u5148\u5C06\u5176\u8F6C\u6362\u4E3A\u4E94\u5B57\u6BB5 cron \u8868\u8FBE\u5F0F\uFF08\u5206 \u65F6 \u65E5 \u6708 \u5468\uFF09\uFF0C\u518D\u8C03\u7528\u5DE5\u5177\u3002\u5F53\u524D\u65F6\u95F4\u4F1A\u63D0\u4F9B\u7ED9\u4F60\u3002\u68C0\u7D22\u8BC1\u636E\u4E2D\u7684\u65F6\u95F4\u6233\u662F\u6D88\u606F\u88AB\u53D1\u9001\u65F6\u7684\u771F\u5B9E\u65F6\u95F4\u3002\u56DE\u7B54\u65F6\u82E5\u6D89\u53CA\u76F8\u5BF9\u65F6\u95F4\u8868\u8FF0\uFF08\u5982\u6D88\u606F\u4E2D\u8BF4\u201D\u660E\u5929\u201D\u201D\u4ECA\u665A\u201D\uFF09\uFF0C\u5FC5\u987B\u57FA\u4E8E\u8BC1\u636E\u4E2D\u6BCF\u6761\u6D88\u606F\u7684\u65F6\u95F4\u6233\u63A8\u5BFC\u4E3A\u5177\u4F53\u65E5\u671F\uFF0C\u4E0D\u8981\u7167\u642C\u539F\u6587\u7684\u76F8\u5BF9\u8868\u8FF0\u3002\u5BF9\u4E8E\u4E00\u822C\u95EE\u7B54\uFF0C\u5148\u6309\u9700\u8C03\u7528\u641C\u7D22\u5DE5\u5177\uFF0C\u518D\u57FA\u4E8E\u5DE5\u5177\u8FD4\u56DE\u7684\u8BC1\u636E\u76F4\u63A5\u7ED9\u51FA\u6700\u7EC8\u7B54\u6848\uFF1B\u82E5\u5F15\u7528\u4E86\u68C0\u7D22\u7ED3\u679C\uFF0C\u8981\u5728\u7B54\u6848\u91CC\u76F4\u63A5\u5199\u51FA\u5F15\u7528\u5185\u5BB9\u3002\u4E0D\u8981\u58F0\u79F0\u5B8C\u6210\u4E86\u672A\u5B9E\u9645\u8C03\u7528\u7684\u64CD\u4F5C\u3002\u91CD\u8981\uFF1A\u4F60\u7684\u56DE\u7B54\u5FC5\u987B\u662F\u9762\u5411\u7FA4\u6210\u5458\u7684\u81EA\u7136\u8BED\u8A00\uFF0C\u7EDD\u5BF9\u4E0D\u80FD\u8F93\u51FA JSON\u3001\u5DE5\u5177\u8C03\u7528\u7EC6\u8282\u6216\u539F\u59CB\u7684\u641C\u7D22\u7ED3\u679C\u683C\u5F0F\u3002\u7528\u6237\u53EA\u5E94\u770B\u5230\u4F60\u6574\u5408\u540E\u7684\u6700\u7EC8\u7B54\u6848\u3002`;
|
|
3821
|
+
var FEISHU_TOOL_SYSTEM_PROMPT = `\u4F60\u662F\u98DE\u4E66\u7FA4\u804A\u52A9\u624B\u3002\u4F60\u53EF\u4EE5\u5148\u641C\u7D22\u672C\u5730\u77E5\u8BC6\u6765\u56DE\u7B54\u95EE\u9898\uFF1B\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u521B\u5EFA\u3001\u67E5\u770B\u6216\u5220\u9664\u7FA4\u6D88\u606F\u5B9A\u65F6\u4EFB\u52A1\u65F6\uFF0C\u4E5F\u53EF\u4EE5\u8C03\u7528\u5B9A\u65F6\u4EFB\u52A1\u5DE5\u5177\u3002\u5B9A\u65F6\u4EFB\u52A1\u5DE5\u5177\u53EA\u7BA1\u7406\u5F53\u524D\u7FA4\u804A\uFF0C\u4E0D\u80FD\u8DE8\u7FA4\u64CD\u4F5C\u3002\u82E5\u7528\u6237\u8981\u6C42\u5B9A\u65F6\u4EFB\u52A1\u53D1\u9001\u56FE\u7247\uFF0C\u53EA\u80FD\u4F7F\u7528\u5F53\u524D\u7FA4\u804A\u91CC\u5DF2\u7ECF\u4E0B\u8F7D\u5165\u5E93\u7684\u56FE\u7247\u6587\u4EF6\u540D\uFF0C\u5E76\u5728\u521B\u5EFA\u5B9A\u65F6\u4EFB\u52A1\u65F6\u628A\u6587\u4EF6\u540D\u586B\u5165 imageFileName\uFF1B\u4E0D\u8981\u7F16\u9020\u672C\u5730\u8DEF\u5F84\u3002\u82E5\u7528\u6237\u7528\u81EA\u7136\u8BED\u8A00\u63CF\u8FF0\u65F6\u95F4\uFF0C\u4F60\u9700\u8981\u5148\u5C06\u5176\u8F6C\u6362\u4E3A\u4E94\u5B57\u6BB5 cron \u8868\u8FBE\u5F0F\uFF08\u5206 \u65F6 \u65E5 \u6708 \u5468\uFF09\uFF0C\u518D\u8C03\u7528\u5DE5\u5177\u3002\u5F53\u524D\u65F6\u95F4\u4F1A\u63D0\u4F9B\u7ED9\u4F60\u3002\u68C0\u7D22\u8BC1\u636E\u4E2D\u7684\u65F6\u95F4\u6233\u662F\u6D88\u606F\u88AB\u53D1\u9001\u65F6\u7684\u771F\u5B9E\u65F6\u95F4\u3002\u56DE\u7B54\u65F6\u82E5\u6D89\u53CA\u76F8\u5BF9\u65F6\u95F4\u8868\u8FF0\uFF08\u5982\u6D88\u606F\u4E2D\u8BF4\u201D\u660E\u5929\u201D\u201D\u4ECA\u665A\u201D\uFF09\uFF0C\u5FC5\u987B\u57FA\u4E8E\u8BC1\u636E\u4E2D\u6BCF\u6761\u6D88\u606F\u7684\u65F6\u95F4\u6233\u63A8\u5BFC\u4E3A\u5177\u4F53\u65E5\u671F\uFF0C\u4E0D\u8981\u7167\u642C\u539F\u6587\u7684\u76F8\u5BF9\u8868\u8FF0\u3002\u5BF9\u4E8E\u4E00\u822C\u95EE\u7B54\uFF0C\u5148\u6309\u9700\u8C03\u7528\u641C\u7D22\u5DE5\u5177\uFF0C\u518D\u57FA\u4E8E\u5DE5\u5177\u8FD4\u56DE\u7684\u8BC1\u636E\u76F4\u63A5\u7ED9\u51FA\u6700\u7EC8\u7B54\u6848\uFF1B\u82E5\u5F15\u7528\u4E86\u68C0\u7D22\u7ED3\u679C\uFF0C\u8981\u5728\u7B54\u6848\u91CC\u76F4\u63A5\u5199\u51FA\u5F15\u7528\u5185\u5BB9\u3002\u4E0D\u8981\u58F0\u79F0\u5B8C\u6210\u4E86\u672A\u5B9E\u9645\u8C03\u7528\u7684\u64CD\u4F5C\u3002\u91CD\u8981\uFF1A\u4F60\u7684\u56DE\u7B54\u5FC5\u987B\u662F\u9762\u5411\u7FA4\u6210\u5458\u7684\u81EA\u7136\u8BED\u8A00\uFF0C\u7EDD\u5BF9\u4E0D\u80FD\u8F93\u51FA JSON\u3001\u5DE5\u5177\u8C03\u7528\u7EC6\u8282\u6216\u539F\u59CB\u7684\u641C\u7D22\u7ED3\u679C\u683C\u5F0F\u3002\u7528\u6237\u53EA\u5E94\u770B\u5230\u4F60\u6574\u5408\u540E\u7684\u6700\u7EC8\u7B54\u6848\u3002`;
|
|
3777
3822
|
var DEFAULT_MAX_MODEL_TURNS = 4;
|
|
3778
3823
|
var DEFAULT_MAX_TOOL_CALLS = 8;
|
|
3779
3824
|
var FEISHU_TOOL_LOOP_FALLBACK = "\u5B9A\u65F6\u4EFB\u52A1\u64CD\u4F5C\u5DF2\u63D0\u4EA4\uFF0C\u4F46\u6A21\u578B\u6CA1\u6709\u751F\u6210\u6700\u7EC8\u56DE\u590D\u3002";
|
|
@@ -4012,9 +4057,22 @@ var FeishuQuestionHandler = class {
|
|
|
4012
4057
|
|
|
4013
4058
|
// src/feishu/sender.ts
|
|
4014
4059
|
import * as lark from "@larksuiteoapi/node-sdk";
|
|
4060
|
+
import fs9 from "fs/promises";
|
|
4015
4061
|
function mapDomain(domain) {
|
|
4016
4062
|
return domain === "lark" ? lark.Domain.Lark : lark.Domain.Feishu;
|
|
4017
4063
|
}
|
|
4064
|
+
function extractImageKey(response) {
|
|
4065
|
+
const data2 = response && typeof response === "object" ? response : {};
|
|
4066
|
+
const direct = data2.image_key;
|
|
4067
|
+
if (typeof direct === "string" && direct.trim()) {
|
|
4068
|
+
return direct.trim();
|
|
4069
|
+
}
|
|
4070
|
+
const nested = data2.data && typeof data2.data === "object" ? data2.data.image_key : void 0;
|
|
4071
|
+
if (typeof nested === "string" && nested.trim()) {
|
|
4072
|
+
return nested.trim();
|
|
4073
|
+
}
|
|
4074
|
+
throw new Error("\u98DE\u4E66\u56FE\u7247\u4E0A\u4F20\u54CD\u5E94\u7F3A\u5C11 image_key\u3002");
|
|
4075
|
+
}
|
|
4018
4076
|
var FeishuMessageSender = class _FeishuMessageSender {
|
|
4019
4077
|
constructor(client) {
|
|
4020
4078
|
this.client = client;
|
|
@@ -4051,6 +4109,39 @@ var FeishuMessageSender = class _FeishuMessageSender {
|
|
|
4051
4109
|
throw new Error("\u5F53\u524D\u98DE\u4E66 SDK \u4E0D\u652F\u6301\u6D88\u606F\u53D1\u9001\u63A5\u53E3\u3002");
|
|
4052
4110
|
}
|
|
4053
4111
|
}
|
|
4112
|
+
async sendImageToChat(chatId, imagePath) {
|
|
4113
|
+
const imageCreate = this.client.im.v1?.image?.create;
|
|
4114
|
+
if (!imageCreate) {
|
|
4115
|
+
throw new Error("\u5F53\u524D\u98DE\u4E66 SDK \u4E0D\u652F\u6301\u56FE\u7247\u4E0A\u4F20\u63A5\u53E3\u3002");
|
|
4116
|
+
}
|
|
4117
|
+
const image = await fs9.readFile(imagePath);
|
|
4118
|
+
const uploaded = await imageCreate({
|
|
4119
|
+
data: {
|
|
4120
|
+
image_type: "message",
|
|
4121
|
+
image
|
|
4122
|
+
}
|
|
4123
|
+
});
|
|
4124
|
+
const imageKey = extractImageKey(uploaded);
|
|
4125
|
+
const payload = {
|
|
4126
|
+
data: {
|
|
4127
|
+
receive_id: chatId,
|
|
4128
|
+
msg_type: "image",
|
|
4129
|
+
content: JSON.stringify({ image_key: imageKey })
|
|
4130
|
+
},
|
|
4131
|
+
params: {
|
|
4132
|
+
receive_id_type: "chat_id"
|
|
4133
|
+
}
|
|
4134
|
+
};
|
|
4135
|
+
if (this.client.im.v1?.message.create) {
|
|
4136
|
+
await this.client.im.v1.message.create(payload);
|
|
4137
|
+
return;
|
|
4138
|
+
}
|
|
4139
|
+
if (this.client.im.message?.create) {
|
|
4140
|
+
await this.client.im.message.create(payload);
|
|
4141
|
+
return;
|
|
4142
|
+
}
|
|
4143
|
+
throw new Error("\u5F53\u524D\u98DE\u4E66 SDK \u4E0D\u652F\u6301\u6D88\u606F\u53D1\u9001\u63A5\u53E3\u3002");
|
|
4144
|
+
}
|
|
4054
4145
|
async replyTextToMessage(messageId, text) {
|
|
4055
4146
|
const payload = {
|
|
4056
4147
|
path: {
|
|
@@ -4191,6 +4282,13 @@ function createFeishuEventDispatcher(options) {
|
|
|
4191
4282
|
}
|
|
4192
4283
|
});
|
|
4193
4284
|
}
|
|
4285
|
+
function resolveFeishuImagePath(config, imageFileName) {
|
|
4286
|
+
const fileName = path12.basename(imageFileName.trim());
|
|
4287
|
+
if (!fileName || fileName !== imageFileName.trim()) {
|
|
4288
|
+
throw new Error("\u56FE\u7247\u6587\u4EF6\u540D\u65E0\u6548\u3002");
|
|
4289
|
+
}
|
|
4290
|
+
return path12.join(resolveHomePath(config.storage.dataDir), "files", "feishu", fileName);
|
|
4291
|
+
}
|
|
4194
4292
|
function createFeishuGateway(options) {
|
|
4195
4293
|
assertFeishuConfig(options.config, options.secrets);
|
|
4196
4294
|
const wsClient = options.wsClientFactory?.({
|
|
@@ -4236,6 +4334,10 @@ function createFeishuGateway(options) {
|
|
|
4236
4334
|
const cronJobScheduler = options.cronJobScheduler ?? (options.cronJobProcessor ? createCronJobScheduler({
|
|
4237
4335
|
repository: new CronJobRepository(options.cronJobProcessor.database),
|
|
4238
4336
|
sendTextToChat: (chatId, text) => options.cronJobProcessor.sender.sendTextToChat(chatId, text),
|
|
4337
|
+
sendImageToChat: options.cronJobProcessor.sender.sendImageToChat ? (chatId, imageFileName) => options.cronJobProcessor.sender.sendImageToChat(
|
|
4338
|
+
chatId,
|
|
4339
|
+
resolveFeishuImagePath(options.config, imageFileName)
|
|
4340
|
+
) : void 0,
|
|
4239
4341
|
generateMessage: async (job, now) => {
|
|
4240
4342
|
const { tools, close } = await createAgenticRagSearchTools({
|
|
4241
4343
|
config: options.config,
|
|
@@ -4273,8 +4375,8 @@ function createFeishuGateway(options) {
|
|
|
4273
4375
|
|
|
4274
4376
|
// src/feishu/resource-downloader.ts
|
|
4275
4377
|
import * as lark3 from "@larksuiteoapi/node-sdk";
|
|
4276
|
-
import
|
|
4277
|
-
import
|
|
4378
|
+
import fs10 from "fs/promises";
|
|
4379
|
+
import path13 from "path";
|
|
4278
4380
|
var RESOURCE_TYPE_BY_KIND = {
|
|
4279
4381
|
file: "file",
|
|
4280
4382
|
image: "image",
|
|
@@ -4312,10 +4414,10 @@ var FeishuResourceDownloader = class _FeishuResourceDownloader {
|
|
|
4312
4414
|
}
|
|
4313
4415
|
async download(input2) {
|
|
4314
4416
|
const resourceType = RESOURCE_TYPE_BY_KIND[input2.attachment.kind];
|
|
4315
|
-
const targetDir =
|
|
4316
|
-
await
|
|
4417
|
+
const targetDir = path13.join(this.dataDir, "files", "feishu");
|
|
4418
|
+
await fs10.mkdir(targetDir, { recursive: true });
|
|
4317
4419
|
const fileName = buildStoredFileName(input2);
|
|
4318
|
-
const storedPath =
|
|
4420
|
+
const storedPath = path13.join(targetDir, fileName);
|
|
4319
4421
|
const payload = {
|
|
4320
4422
|
params: { type: resourceType },
|
|
4321
4423
|
path: { message_id: input2.messageId, file_key: input2.attachment.fileKey }
|
|
@@ -4338,29 +4440,29 @@ var FeishuResourceDownloader = class _FeishuResourceDownloader {
|
|
|
4338
4440
|
|
|
4339
4441
|
// src/files/ingest.ts
|
|
4340
4442
|
import crypto7 from "crypto";
|
|
4341
|
-
import
|
|
4342
|
-
import
|
|
4443
|
+
import fs12 from "fs/promises";
|
|
4444
|
+
import path15 from "path";
|
|
4343
4445
|
|
|
4344
4446
|
// src/files/parser.ts
|
|
4345
|
-
import
|
|
4346
|
-
import
|
|
4447
|
+
import fs11 from "fs/promises";
|
|
4448
|
+
import path14 from "path";
|
|
4347
4449
|
import mammoth from "mammoth";
|
|
4348
4450
|
import { PDFParse } from "pdf-parse";
|
|
4349
4451
|
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".txt", ".md", ".markdown", ".json", ".csv", ".tsv", ".log"]);
|
|
4350
4452
|
var DOCX_EXTENSIONS = /* @__PURE__ */ new Set([".docx"]);
|
|
4351
4453
|
var PDF_EXTENSIONS = /* @__PURE__ */ new Set([".pdf"]);
|
|
4352
4454
|
function isSupportedParseFile(filePath) {
|
|
4353
|
-
const extension =
|
|
4455
|
+
const extension = path14.extname(filePath).toLowerCase();
|
|
4354
4456
|
return TEXT_EXTENSIONS.has(extension) || DOCX_EXTENSIONS.has(extension) || PDF_EXTENSIONS.has(extension);
|
|
4355
4457
|
}
|
|
4356
4458
|
function describeSupportedParseTypes() {
|
|
4357
4459
|
return "txt\u3001md\u3001json\u3001csv\u3001tsv\u3001log\u3001docx\u3001pdf";
|
|
4358
4460
|
}
|
|
4359
4461
|
async function parseFileToText(filePath) {
|
|
4360
|
-
const extension =
|
|
4462
|
+
const extension = path14.extname(filePath).toLowerCase();
|
|
4361
4463
|
if (TEXT_EXTENSIONS.has(extension)) {
|
|
4362
4464
|
return {
|
|
4363
|
-
text: await
|
|
4465
|
+
text: await fs11.readFile(filePath, "utf8"),
|
|
4364
4466
|
parser: "text",
|
|
4365
4467
|
warnings: []
|
|
4366
4468
|
};
|
|
@@ -4374,7 +4476,7 @@ async function parseFileToText(filePath) {
|
|
|
4374
4476
|
};
|
|
4375
4477
|
}
|
|
4376
4478
|
if (PDF_EXTENSIONS.has(extension)) {
|
|
4377
|
-
const buffer = await
|
|
4479
|
+
const buffer = await fs11.readFile(filePath);
|
|
4378
4480
|
const parser = new PDFParse({ data: buffer });
|
|
4379
4481
|
try {
|
|
4380
4482
|
const result = await parser.getText();
|
|
@@ -4396,7 +4498,7 @@ function isSupportedTextFile(filePath) {
|
|
|
4396
4498
|
}
|
|
4397
4499
|
function ensureSupportedTextFile(filePath) {
|
|
4398
4500
|
if (!isSupportedTextFile(filePath)) {
|
|
4399
|
-
const extension =
|
|
4501
|
+
const extension = path15.extname(filePath).toLowerCase();
|
|
4400
4502
|
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`);
|
|
4401
4503
|
}
|
|
4402
4504
|
}
|
|
@@ -4405,12 +4507,12 @@ function stableStoredName(sourcePath, fileName) {
|
|
|
4405
4507
|
return `${digest}-${fileName}`;
|
|
4406
4508
|
}
|
|
4407
4509
|
async function ingestLocalFile(input2) {
|
|
4408
|
-
const sourcePath =
|
|
4409
|
-
const fileName =
|
|
4510
|
+
const sourcePath = path15.resolve(input2.filePath);
|
|
4511
|
+
const fileName = path15.basename(sourcePath);
|
|
4410
4512
|
const jobId = input2.jobs?.start({ sourcePath, fileName });
|
|
4411
4513
|
try {
|
|
4412
4514
|
ensureSupportedTextFile(sourcePath);
|
|
4413
|
-
const stat = await
|
|
4515
|
+
const stat = await fs12.stat(sourcePath);
|
|
4414
4516
|
if (!stat.isFile()) {
|
|
4415
4517
|
throw new Error(`\u4E0D\u662F\u6587\u4EF6\uFF1A${sourcePath}`);
|
|
4416
4518
|
}
|
|
@@ -4419,10 +4521,10 @@ async function ingestLocalFile(input2) {
|
|
|
4419
4521
|
if (!text) {
|
|
4420
4522
|
throw new Error(`\u6587\u4EF6\u6CA1\u6709\u53EF\u7D22\u5F15\u6587\u672C\uFF1A${sourcePath}`);
|
|
4421
4523
|
}
|
|
4422
|
-
const fileDir =
|
|
4423
|
-
await
|
|
4424
|
-
const storedPath =
|
|
4425
|
-
await
|
|
4524
|
+
const fileDir = path15.join(resolveHomePath(input2.config.storage.dataDir), "files");
|
|
4525
|
+
await fs12.mkdir(fileDir, { recursive: true });
|
|
4526
|
+
const storedPath = path15.join(fileDir, stableStoredName(sourcePath, fileName));
|
|
4527
|
+
await fs12.copyFile(sourcePath, storedPath);
|
|
4426
4528
|
const messageId = input2.messages.ingest({
|
|
4427
4529
|
platform: "local-file",
|
|
4428
4530
|
platformChatId: "local-files",
|
|
@@ -4727,8 +4829,8 @@ var GatewayIngestor = class {
|
|
|
4727
4829
|
|
|
4728
4830
|
// src/gateway/detached.ts
|
|
4729
4831
|
import { spawn } from "child_process";
|
|
4730
|
-
import
|
|
4731
|
-
import
|
|
4832
|
+
import fs13 from "fs";
|
|
4833
|
+
import path16 from "path";
|
|
4732
4834
|
var START_FAILURE_GRACE_MS = 250;
|
|
4733
4835
|
function buildGatewayForegroundSpawnCommand(argv = process.argv) {
|
|
4734
4836
|
const [command = process.execPath, ...rawArgs] = argv;
|
|
@@ -4785,7 +4887,7 @@ async function startDetachedGateway(input2) {
|
|
|
4785
4887
|
...status.pid ? { pid: status.pid } : {}
|
|
4786
4888
|
};
|
|
4787
4889
|
}
|
|
4788
|
-
|
|
4890
|
+
fs13.mkdirSync(path16.dirname(logFile), { recursive: true });
|
|
4789
4891
|
let out;
|
|
4790
4892
|
let err;
|
|
4791
4893
|
let stdioClosed = false;
|
|
@@ -4795,15 +4897,15 @@ async function startDetachedGateway(input2) {
|
|
|
4795
4897
|
}
|
|
4796
4898
|
stdioClosed = true;
|
|
4797
4899
|
if (typeof out === "number") {
|
|
4798
|
-
|
|
4900
|
+
fs13.closeSync(out);
|
|
4799
4901
|
}
|
|
4800
4902
|
if (typeof err === "number") {
|
|
4801
|
-
|
|
4903
|
+
fs13.closeSync(err);
|
|
4802
4904
|
}
|
|
4803
4905
|
};
|
|
4804
4906
|
try {
|
|
4805
|
-
out =
|
|
4806
|
-
err =
|
|
4907
|
+
out = fs13.openSync(logFile, "a");
|
|
4908
|
+
err = fs13.openSync(logFile, "a");
|
|
4807
4909
|
const foreground = buildGatewayForegroundSpawnCommand(input2.argv);
|
|
4808
4910
|
const child = spawn(foreground.command, foreground.args, {
|
|
4809
4911
|
detached: true,
|
|
@@ -4834,7 +4936,7 @@ async function startDetachedGateway(input2) {
|
|
|
4834
4936
|
}
|
|
4835
4937
|
|
|
4836
4938
|
// src/multimodal/openai-compatible.ts
|
|
4837
|
-
import
|
|
4939
|
+
import fs14 from "fs/promises";
|
|
4838
4940
|
function normalizeBaseUrl2(baseUrl) {
|
|
4839
4941
|
return baseUrl.replace(/\/+$/, "");
|
|
4840
4942
|
}
|
|
@@ -4844,6 +4946,7 @@ function buildPrompt(context) {
|
|
|
4844
4946
|
"\u8BF7\u7406\u89E3\u8FD9\u5F20\u56FE\u7247\uFF0C\u5224\u65AD\u5B83\u662F\u5426\u5305\u542B\u503C\u5F97\u8FDB\u5165\u77E5\u8BC6\u5E93\u548C\u4F1A\u8BDD\u8BB0\u5FC6\u7684\u6709\u610F\u4E49\u4FE1\u606F\u3002",
|
|
4845
4947
|
'\u8BF7\u53EA\u8F93\u51FA JSON\uFF0C\u683C\u5F0F\u4E3A {"summary": string, "isMeaningful": boolean, "reason": string}\u3002',
|
|
4846
4948
|
"summary \u4F7F\u7528\u7B80\u6D01\u4E2D\u6587\u8F6C\u8FF0\u56FE\u7247\u4E2D\u7684\u5173\u952E\u4FE1\u606F\uFF1B\u65E0\u610F\u4E49\u56FE\u7247\u4E5F\u8981\u7ED9\u51FA\u7B80\u77ED summary\u3002",
|
|
4949
|
+
"\u5982\u679C\u4E0A\u4E0B\u6587\u63D0\u4F9B\u4E86\u56FE\u7247\u6587\u4EF6\u540D\uFF0Csummary \u5FC5\u987B\u539F\u6837\u5305\u542B\u8BE5\u6587\u4EF6\u540D\uFF0C\u4FBF\u4E8E\u4E4B\u540E\u6309\u6587\u4EF6\u540D\u68C0\u7D22\u548C\u53D1\u9001\u56FE\u7247\u3002",
|
|
4847
4950
|
contextText ? `\u4E0A\u4E0B\u6587\uFF1A${contextText}` : void 0
|
|
4848
4951
|
].filter(Boolean).join("\n");
|
|
4849
4952
|
}
|
|
@@ -4881,7 +4984,7 @@ var OpenAICompatibleMultimodalModel = class {
|
|
|
4881
4984
|
if (!this.options.baseUrl || !this.options.apiKey || !this.options.model) {
|
|
4882
4985
|
throw new Error("\u591A\u6A21\u6001\u914D\u7F6E\u4E0D\u5B8C\u6574\u3002\u8BF7\u8FD0\u884C chattercatcher setup \u6216 chattercatcher settings\u3002");
|
|
4883
4986
|
}
|
|
4884
|
-
const image = await
|
|
4987
|
+
const image = await fs14.readFile(input2.imagePath);
|
|
4885
4988
|
const response = await fetch(`${normalizeBaseUrl2(this.options.baseUrl)}/chat/completions`, {
|
|
4886
4989
|
method: "POST",
|
|
4887
4990
|
headers: {
|
|
@@ -6415,7 +6518,7 @@ dev.command("ingest-feishu-event").description("\u4ECE JSON \u6587\u4EF6\u6A21\u
|
|
|
6415
6518
|
const config = await loadConfig();
|
|
6416
6519
|
const database = openDatabase(config);
|
|
6417
6520
|
try {
|
|
6418
|
-
const raw = await
|
|
6521
|
+
const raw = await fs15.readFile(options.file, "utf8");
|
|
6419
6522
|
const payload = JSON.parse(raw);
|
|
6420
6523
|
const result = new GatewayIngestor(database).ingestFeishuEvent(payload);
|
|
6421
6524
|
if (!result.accepted) {
|