@lih-x-x/kmr 1.0.3 → 1.0.5
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/index.js +101 -63
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24,6 +24,11 @@ function createEventDispatcher(onMessage) {
|
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
const messageId = message.message_id;
|
|
27
|
+
const senderType = data.sender?.sender_type;
|
|
28
|
+
if (senderType === "app") {
|
|
29
|
+
console.log(`[recv] \u5FFD\u7565\u673A\u5668\u4EBA\u81EA\u8EAB\u6D88\u606F: ${messageId}`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
27
32
|
if (processedMessages.has(messageId)) {
|
|
28
33
|
console.log(`[dedup] \u8DF3\u8FC7\u91CD\u590D\u6D88\u606F: ${messageId}`);
|
|
29
34
|
return;
|
|
@@ -398,6 +403,36 @@ var RANK_RESULTS_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u641C\u7D22\u6392\u5E8F\u52A
|
|
|
398
403
|
|
|
399
404
|
\u7528\u6237\u67E5\u8BE2\uFF1A`;
|
|
400
405
|
|
|
406
|
+
// src/utils/claudeEnv.ts
|
|
407
|
+
import fs from "fs";
|
|
408
|
+
import path from "path";
|
|
409
|
+
import os from "os";
|
|
410
|
+
var cachedEnv = null;
|
|
411
|
+
function getClaudeEnv() {
|
|
412
|
+
if (cachedEnv) return cachedEnv;
|
|
413
|
+
const settingsPath = path.join(os.homedir(), ".claude", "settings.json");
|
|
414
|
+
let settingsEnv = {};
|
|
415
|
+
try {
|
|
416
|
+
const content = fs.readFileSync(settingsPath, "utf-8");
|
|
417
|
+
const settings = JSON.parse(content);
|
|
418
|
+
if (settings.env && typeof settings.env === "object") {
|
|
419
|
+
settingsEnv = settings.env;
|
|
420
|
+
}
|
|
421
|
+
} catch {
|
|
422
|
+
}
|
|
423
|
+
cachedEnv = { ...settingsEnv };
|
|
424
|
+
for (const key of Object.keys(settingsEnv)) {
|
|
425
|
+
if (process.env[key]) {
|
|
426
|
+
cachedEnv[key] = process.env[key];
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return cachedEnv;
|
|
430
|
+
}
|
|
431
|
+
function getExecEnv() {
|
|
432
|
+
const claudeEnv = getClaudeEnv();
|
|
433
|
+
return { ...process.env, ...claudeEnv };
|
|
434
|
+
}
|
|
435
|
+
|
|
401
436
|
// src/agent/claudeCode.ts
|
|
402
437
|
var execFileAsync = promisify(execFile);
|
|
403
438
|
var ClaudeCodeProvider = class {
|
|
@@ -448,7 +483,8 @@ ${prompt}
|
|
|
448
483
|
try {
|
|
449
484
|
const { stdout } = await execFileAsync("acpx", ["--allowed-tools", "", "claude", "exec", prompt], {
|
|
450
485
|
timeout: this.timeout,
|
|
451
|
-
maxBuffer: 1024 * 1024
|
|
486
|
+
maxBuffer: 1024 * 1024,
|
|
487
|
+
env: getExecEnv()
|
|
452
488
|
});
|
|
453
489
|
return this.extractJson(stdout);
|
|
454
490
|
} catch (err) {
|
|
@@ -489,31 +525,31 @@ ${prompt}
|
|
|
489
525
|
};
|
|
490
526
|
|
|
491
527
|
// src/storage/jsonStore.ts
|
|
492
|
-
import
|
|
493
|
-
import
|
|
528
|
+
import fs2 from "fs";
|
|
529
|
+
import path2 from "path";
|
|
494
530
|
var JsonStore = class {
|
|
495
531
|
constructor(dataDir) {
|
|
496
532
|
this.dataDir = dataDir;
|
|
497
|
-
|
|
533
|
+
fs2.mkdirSync(dataDir, { recursive: true });
|
|
498
534
|
}
|
|
499
535
|
dataDir;
|
|
500
536
|
async save(record) {
|
|
501
537
|
const date = record.summary.date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
502
538
|
const fileName = `${date}_${record.id}.json`;
|
|
503
|
-
const filePath =
|
|
504
|
-
|
|
539
|
+
const filePath = path2.join(this.dataDir, fileName);
|
|
540
|
+
fs2.writeFileSync(filePath, JSON.stringify(record, null, 2), "utf-8");
|
|
505
541
|
return filePath;
|
|
506
542
|
}
|
|
507
543
|
async load(id) {
|
|
508
544
|
const files = this.getFiles();
|
|
509
545
|
const target = files.find((f) => f.includes(id));
|
|
510
546
|
if (!target) return null;
|
|
511
|
-
const raw =
|
|
547
|
+
const raw = fs2.readFileSync(target, "utf-8");
|
|
512
548
|
return JSON.parse(raw);
|
|
513
549
|
}
|
|
514
550
|
async list() {
|
|
515
551
|
const files = this.getFiles();
|
|
516
|
-
return files.map((f) => JSON.parse(
|
|
552
|
+
return files.map((f) => JSON.parse(fs2.readFileSync(f, "utf-8")));
|
|
517
553
|
}
|
|
518
554
|
async search(keywords) {
|
|
519
555
|
const all = await this.list();
|
|
@@ -526,25 +562,25 @@ var JsonStore = class {
|
|
|
526
562
|
const files = this.getFiles();
|
|
527
563
|
const target = files.find((f) => f.includes(id));
|
|
528
564
|
if (!target) return false;
|
|
529
|
-
|
|
565
|
+
fs2.unlinkSync(target);
|
|
530
566
|
return true;
|
|
531
567
|
}
|
|
532
568
|
getFiles() {
|
|
533
|
-
if (!
|
|
534
|
-
return
|
|
569
|
+
if (!fs2.existsSync(this.dataDir)) return [];
|
|
570
|
+
return fs2.readdirSync(this.dataDir).filter((f) => f.endsWith(".json")).map((f) => path2.join(this.dataDir, f));
|
|
535
571
|
}
|
|
536
572
|
};
|
|
537
573
|
|
|
538
574
|
// src/query/finder.ts
|
|
539
|
-
import
|
|
540
|
-
import
|
|
575
|
+
import fs3 from "fs";
|
|
576
|
+
import path3 from "path";
|
|
541
577
|
async function grepMeetings(dataDir, keywords) {
|
|
542
|
-
if (!
|
|
543
|
-
const files =
|
|
578
|
+
if (!fs3.existsSync(dataDir)) return [];
|
|
579
|
+
const files = fs3.readdirSync(dataDir).filter((f) => f.endsWith(".json"));
|
|
544
580
|
const matches = [];
|
|
545
581
|
for (const file of files) {
|
|
546
|
-
const filePath =
|
|
547
|
-
const raw =
|
|
582
|
+
const filePath = path3.join(dataDir, file);
|
|
583
|
+
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
548
584
|
const hasMatch = keywords.some((kw) => raw.includes(kw));
|
|
549
585
|
if (hasMatch) {
|
|
550
586
|
matches.push(JSON.parse(raw));
|
|
@@ -572,11 +608,11 @@ var QueryHandler = class {
|
|
|
572
608
|
|
|
573
609
|
// src/web/server.ts
|
|
574
610
|
import http from "http";
|
|
575
|
-
import
|
|
576
|
-
import
|
|
611
|
+
import fs4 from "fs";
|
|
612
|
+
import path4 from "path";
|
|
577
613
|
import { fileURLToPath } from "url";
|
|
578
|
-
var __dirname =
|
|
579
|
-
var PUBLIC_DIR =
|
|
614
|
+
var __dirname = path4.dirname(fileURLToPath(import.meta.url));
|
|
615
|
+
var PUBLIC_DIR = path4.join(__dirname, "public");
|
|
580
616
|
var MIME_TYPES = {
|
|
581
617
|
".html": "text/html; charset=utf-8",
|
|
582
618
|
".css": "text/css; charset=utf-8",
|
|
@@ -586,30 +622,30 @@ var MIME_TYPES = {
|
|
|
586
622
|
".svg": "image/svg+xml"
|
|
587
623
|
};
|
|
588
624
|
function serveStatic(res, urlPath) {
|
|
589
|
-
const safePath =
|
|
590
|
-
let filePath =
|
|
591
|
-
if (!
|
|
592
|
-
filePath =
|
|
625
|
+
const safePath = path4.normalize(urlPath).replace(/^(\.\.[/\\])+/, "");
|
|
626
|
+
let filePath = path4.join(PUBLIC_DIR, safePath === "/" ? "index.html" : safePath);
|
|
627
|
+
if (!fs4.existsSync(filePath) || fs4.statSync(filePath).isDirectory()) {
|
|
628
|
+
filePath = path4.join(PUBLIC_DIR, "index.html");
|
|
593
629
|
}
|
|
594
|
-
const ext =
|
|
630
|
+
const ext = path4.extname(filePath);
|
|
595
631
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
596
|
-
const content =
|
|
632
|
+
const content = fs4.readFileSync(filePath);
|
|
597
633
|
res.writeHead(200, { "Content-Type": contentType });
|
|
598
634
|
res.end(content);
|
|
599
635
|
}
|
|
600
636
|
function listSessions(kmrDir) {
|
|
601
|
-
const sessionsDir =
|
|
602
|
-
if (!
|
|
603
|
-
return
|
|
604
|
-
const full =
|
|
605
|
-
return
|
|
637
|
+
const sessionsDir = path4.join(kmrDir, "sessions");
|
|
638
|
+
if (!fs4.existsSync(sessionsDir)) return [];
|
|
639
|
+
return fs4.readdirSync(sessionsDir).filter((d) => {
|
|
640
|
+
const full = path4.join(sessionsDir, d);
|
|
641
|
+
return fs4.statSync(full).isDirectory();
|
|
606
642
|
}).map((userId) => {
|
|
607
|
-
const summaryPath =
|
|
643
|
+
const summaryPath = path4.join(sessionsDir, userId, "summary.md");
|
|
608
644
|
let summaryPreview = "";
|
|
609
645
|
let messageCount = 0;
|
|
610
646
|
let lastActivity = "";
|
|
611
|
-
if (
|
|
612
|
-
const content =
|
|
647
|
+
if (fs4.existsSync(summaryPath)) {
|
|
648
|
+
const content = fs4.readFileSync(summaryPath, "utf-8");
|
|
613
649
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
614
650
|
messageCount = lines.length;
|
|
615
651
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
@@ -621,26 +657,26 @@ function listSessions(kmrDir) {
|
|
|
621
657
|
const lastLine = lines[lines.length - 1] || "";
|
|
622
658
|
const timeMatch = lastLine.match(/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]/);
|
|
623
659
|
if (timeMatch) lastActivity = timeMatch[1];
|
|
624
|
-
const stat =
|
|
660
|
+
const stat = fs4.statSync(summaryPath);
|
|
625
661
|
if (!lastActivity) lastActivity = stat.mtime.toISOString().replace("T", " ").slice(0, 19);
|
|
626
662
|
} else {
|
|
627
|
-
const userDir =
|
|
628
|
-
const stat =
|
|
663
|
+
const userDir = path4.join(sessionsDir, userId);
|
|
664
|
+
const stat = fs4.statSync(userDir);
|
|
629
665
|
lastActivity = stat.mtime.toISOString().replace("T", " ").slice(0, 19);
|
|
630
666
|
}
|
|
631
667
|
return { userId, summaryPreview, messageCount, lastActivity };
|
|
632
668
|
}).sort((a, b) => b.lastActivity.localeCompare(a.lastActivity));
|
|
633
669
|
}
|
|
634
670
|
function deleteSession(kmrDir, userId) {
|
|
635
|
-
const sessionDir =
|
|
636
|
-
if (!
|
|
637
|
-
|
|
671
|
+
const sessionDir = path4.join(kmrDir, "sessions", userId);
|
|
672
|
+
if (!fs4.existsSync(sessionDir)) return false;
|
|
673
|
+
fs4.rmSync(sessionDir, { recursive: true, force: true });
|
|
638
674
|
return true;
|
|
639
675
|
}
|
|
640
676
|
function getSessionDetail(kmrDir, userId) {
|
|
641
|
-
const summaryPath =
|
|
642
|
-
if (!
|
|
643
|
-
return
|
|
677
|
+
const summaryPath = path4.join(kmrDir, "sessions", userId, "summary.md");
|
|
678
|
+
if (!fs4.existsSync(summaryPath)) return null;
|
|
679
|
+
return fs4.readFileSync(summaryPath, "utf-8");
|
|
644
680
|
}
|
|
645
681
|
function startWebServer(store, kmrDir, port = 3e3) {
|
|
646
682
|
return new Promise((resolve, reject) => {
|
|
@@ -710,8 +746,8 @@ async function handleApi(req, res, url, store, kmrDir) {
|
|
|
710
746
|
return;
|
|
711
747
|
}
|
|
712
748
|
if (pathname === "/api/config" && method === "GET") {
|
|
713
|
-
const configPath =
|
|
714
|
-
const config = JSON.parse(
|
|
749
|
+
const configPath = path4.join(kmrDir, "config.json");
|
|
750
|
+
const config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
|
|
715
751
|
jsonResponse(res, 200, { ok: true, data: config });
|
|
716
752
|
return;
|
|
717
753
|
}
|
|
@@ -724,8 +760,8 @@ async function handleApi(req, res, url, store, kmrDir) {
|
|
|
724
760
|
jsonResponse(res, 400, { ok: false, error: "\u65E0\u6548\u7684 JSON \u683C\u5F0F" });
|
|
725
761
|
return;
|
|
726
762
|
}
|
|
727
|
-
const configPath =
|
|
728
|
-
|
|
763
|
+
const configPath = path4.join(kmrDir, "config.json");
|
|
764
|
+
fs4.writeFileSync(configPath, JSON.stringify(parsed, null, 2), "utf-8");
|
|
729
765
|
jsonResponse(res, 200, { ok: true, data: parsed });
|
|
730
766
|
return;
|
|
731
767
|
}
|
|
@@ -779,8 +815,8 @@ function openBrowser(url) {
|
|
|
779
815
|
// src/session/manager.ts
|
|
780
816
|
import { execFile as execFile3 } from "child_process";
|
|
781
817
|
import { promisify as promisify2 } from "util";
|
|
782
|
-
import
|
|
783
|
-
import
|
|
818
|
+
import fs5 from "fs";
|
|
819
|
+
import path5 from "path";
|
|
784
820
|
|
|
785
821
|
// src/session/skill.ts
|
|
786
822
|
var SESSION_SKILL = `\u4F60\u662F KMR\uFF08Key Meetings Record\uFF09\u7684\u667A\u80FD\u52A9\u624B\u3002
|
|
@@ -808,8 +844,8 @@ var execFileAsync2 = promisify2(execFile3);
|
|
|
808
844
|
var SessionManager = class {
|
|
809
845
|
constructor(kmrDir, timeout = 6e4) {
|
|
810
846
|
this.timeout = timeout;
|
|
811
|
-
this.sessionDir =
|
|
812
|
-
|
|
847
|
+
this.sessionDir = path5.join(kmrDir, "sessions");
|
|
848
|
+
fs5.mkdirSync(this.sessionDir, { recursive: true });
|
|
813
849
|
}
|
|
814
850
|
timeout;
|
|
815
851
|
activeSessions = /* @__PURE__ */ new Map();
|
|
@@ -828,15 +864,16 @@ var SessionManager = class {
|
|
|
828
864
|
return existing.name;
|
|
829
865
|
}
|
|
830
866
|
const sessionName = `kmr-${userId}`;
|
|
831
|
-
const userDir =
|
|
832
|
-
|
|
833
|
-
const skillPath =
|
|
834
|
-
|
|
867
|
+
const userDir = path5.join(this.sessionDir, userId);
|
|
868
|
+
fs5.mkdirSync(userDir, { recursive: true });
|
|
869
|
+
const skillPath = path5.join(userDir, "skill.md");
|
|
870
|
+
fs5.writeFileSync(skillPath, SESSION_SKILL, "utf-8");
|
|
835
871
|
console.log(`[session] \u521B\u5EFA\u65B0 session: ${sessionName}`);
|
|
836
872
|
try {
|
|
837
873
|
await execFileAsync2("acpx", ["claude", "sessions", "ensure", "--name", sessionName], {
|
|
838
874
|
timeout: this.timeout,
|
|
839
|
-
maxBuffer: 1024 * 1024
|
|
875
|
+
maxBuffer: 1024 * 1024,
|
|
876
|
+
env: getExecEnv()
|
|
840
877
|
});
|
|
841
878
|
} catch (err) {
|
|
842
879
|
console.error(`[session] \u521B\u5EFA session \u5931\u8D25:`, err.message);
|
|
@@ -847,7 +884,7 @@ var SessionManager = class {
|
|
|
847
884
|
await execFileAsync2(
|
|
848
885
|
"acpx",
|
|
849
886
|
["--approve-all", "claude", "-s", sessionName, SESSION_SKILL],
|
|
850
|
-
{ timeout: this.timeout, maxBuffer: 1024 * 1024 }
|
|
887
|
+
{ timeout: this.timeout, maxBuffer: 1024 * 1024, env: getExecEnv() }
|
|
851
888
|
);
|
|
852
889
|
} catch (err) {
|
|
853
890
|
console.error(`[session] \u89D2\u8272\u8BBE\u5B9A\u5931\u8D25:`, err.message);
|
|
@@ -863,7 +900,7 @@ var SessionManager = class {
|
|
|
863
900
|
const { stdout } = await execFileAsync2(
|
|
864
901
|
"acpx",
|
|
865
902
|
["--approve-all", "claude", "-s", sessionName, text],
|
|
866
|
-
{ timeout: this.timeout, maxBuffer: 1024 * 1024 }
|
|
903
|
+
{ timeout: this.timeout, maxBuffer: 1024 * 1024, env: getExecEnv() }
|
|
867
904
|
);
|
|
868
905
|
return this.extractReply(stdout);
|
|
869
906
|
} catch (err) {
|
|
@@ -882,21 +919,22 @@ var SessionManager = class {
|
|
|
882
919
|
return filtered || output.trim();
|
|
883
920
|
}
|
|
884
921
|
async appendSummary(userId, userText, aiReply) {
|
|
885
|
-
const userDir =
|
|
886
|
-
const summaryPath =
|
|
922
|
+
const userDir = path5.join(this.sessionDir, userId);
|
|
923
|
+
const summaryPath = path5.join(userDir, "summary.md");
|
|
887
924
|
const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
|
|
888
925
|
const entry = `
|
|
889
926
|
[${now}] \u7528\u6237: ${userText}
|
|
890
927
|
[${now}] AI: ${aiReply}
|
|
891
928
|
`;
|
|
892
|
-
|
|
929
|
+
fs5.appendFileSync(summaryPath, entry, "utf-8");
|
|
893
930
|
}
|
|
894
931
|
async closeSession(userId) {
|
|
895
932
|
const info = this.activeSessions.get(userId);
|
|
896
933
|
if (!info) return;
|
|
897
934
|
try {
|
|
898
935
|
await execFileAsync2("acpx", ["claude", "sessions", "close", info.name], {
|
|
899
|
-
timeout: 1e4
|
|
936
|
+
timeout: 1e4,
|
|
937
|
+
env: getExecEnv()
|
|
900
938
|
});
|
|
901
939
|
console.log(`[session] \u5DF2\u5173\u95ED session: ${info.name}`);
|
|
902
940
|
} catch (err) {
|