@lih-x-x/kmr 1.0.2 → 1.0.4
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
CHANGED
|
@@ -398,6 +398,36 @@ var RANK_RESULTS_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u641C\u7D22\u6392\u5E8F\u52A
|
|
|
398
398
|
|
|
399
399
|
\u7528\u6237\u67E5\u8BE2\uFF1A`;
|
|
400
400
|
|
|
401
|
+
// src/utils/claudeEnv.ts
|
|
402
|
+
import fs from "fs";
|
|
403
|
+
import path from "path";
|
|
404
|
+
import os from "os";
|
|
405
|
+
var cachedEnv = null;
|
|
406
|
+
function getClaudeEnv() {
|
|
407
|
+
if (cachedEnv) return cachedEnv;
|
|
408
|
+
const settingsPath = path.join(os.homedir(), ".claude", "settings.json");
|
|
409
|
+
let settingsEnv = {};
|
|
410
|
+
try {
|
|
411
|
+
const content = fs.readFileSync(settingsPath, "utf-8");
|
|
412
|
+
const settings = JSON.parse(content);
|
|
413
|
+
if (settings.env && typeof settings.env === "object") {
|
|
414
|
+
settingsEnv = settings.env;
|
|
415
|
+
}
|
|
416
|
+
} catch {
|
|
417
|
+
}
|
|
418
|
+
cachedEnv = { ...settingsEnv };
|
|
419
|
+
for (const key of Object.keys(settingsEnv)) {
|
|
420
|
+
if (process.env[key]) {
|
|
421
|
+
cachedEnv[key] = process.env[key];
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return cachedEnv;
|
|
425
|
+
}
|
|
426
|
+
function getExecEnv() {
|
|
427
|
+
const claudeEnv = getClaudeEnv();
|
|
428
|
+
return { ...process.env, ...claudeEnv };
|
|
429
|
+
}
|
|
430
|
+
|
|
401
431
|
// src/agent/claudeCode.ts
|
|
402
432
|
var execFileAsync = promisify(execFile);
|
|
403
433
|
var ClaudeCodeProvider = class {
|
|
@@ -448,7 +478,8 @@ ${prompt}
|
|
|
448
478
|
try {
|
|
449
479
|
const { stdout } = await execFileAsync("acpx", ["--allowed-tools", "", "claude", "exec", prompt], {
|
|
450
480
|
timeout: this.timeout,
|
|
451
|
-
maxBuffer: 1024 * 1024
|
|
481
|
+
maxBuffer: 1024 * 1024,
|
|
482
|
+
env: getExecEnv()
|
|
452
483
|
});
|
|
453
484
|
return this.extractJson(stdout);
|
|
454
485
|
} catch (err) {
|
|
@@ -489,31 +520,31 @@ ${prompt}
|
|
|
489
520
|
};
|
|
490
521
|
|
|
491
522
|
// src/storage/jsonStore.ts
|
|
492
|
-
import
|
|
493
|
-
import
|
|
523
|
+
import fs2 from "fs";
|
|
524
|
+
import path2 from "path";
|
|
494
525
|
var JsonStore = class {
|
|
495
526
|
constructor(dataDir) {
|
|
496
527
|
this.dataDir = dataDir;
|
|
497
|
-
|
|
528
|
+
fs2.mkdirSync(dataDir, { recursive: true });
|
|
498
529
|
}
|
|
499
530
|
dataDir;
|
|
500
531
|
async save(record) {
|
|
501
532
|
const date = record.summary.date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
502
533
|
const fileName = `${date}_${record.id}.json`;
|
|
503
|
-
const filePath =
|
|
504
|
-
|
|
534
|
+
const filePath = path2.join(this.dataDir, fileName);
|
|
535
|
+
fs2.writeFileSync(filePath, JSON.stringify(record, null, 2), "utf-8");
|
|
505
536
|
return filePath;
|
|
506
537
|
}
|
|
507
538
|
async load(id) {
|
|
508
539
|
const files = this.getFiles();
|
|
509
540
|
const target = files.find((f) => f.includes(id));
|
|
510
541
|
if (!target) return null;
|
|
511
|
-
const raw =
|
|
542
|
+
const raw = fs2.readFileSync(target, "utf-8");
|
|
512
543
|
return JSON.parse(raw);
|
|
513
544
|
}
|
|
514
545
|
async list() {
|
|
515
546
|
const files = this.getFiles();
|
|
516
|
-
return files.map((f) => JSON.parse(
|
|
547
|
+
return files.map((f) => JSON.parse(fs2.readFileSync(f, "utf-8")));
|
|
517
548
|
}
|
|
518
549
|
async search(keywords) {
|
|
519
550
|
const all = await this.list();
|
|
@@ -526,25 +557,25 @@ var JsonStore = class {
|
|
|
526
557
|
const files = this.getFiles();
|
|
527
558
|
const target = files.find((f) => f.includes(id));
|
|
528
559
|
if (!target) return false;
|
|
529
|
-
|
|
560
|
+
fs2.unlinkSync(target);
|
|
530
561
|
return true;
|
|
531
562
|
}
|
|
532
563
|
getFiles() {
|
|
533
|
-
if (!
|
|
534
|
-
return
|
|
564
|
+
if (!fs2.existsSync(this.dataDir)) return [];
|
|
565
|
+
return fs2.readdirSync(this.dataDir).filter((f) => f.endsWith(".json")).map((f) => path2.join(this.dataDir, f));
|
|
535
566
|
}
|
|
536
567
|
};
|
|
537
568
|
|
|
538
569
|
// src/query/finder.ts
|
|
539
|
-
import
|
|
540
|
-
import
|
|
570
|
+
import fs3 from "fs";
|
|
571
|
+
import path3 from "path";
|
|
541
572
|
async function grepMeetings(dataDir, keywords) {
|
|
542
|
-
if (!
|
|
543
|
-
const files =
|
|
573
|
+
if (!fs3.existsSync(dataDir)) return [];
|
|
574
|
+
const files = fs3.readdirSync(dataDir).filter((f) => f.endsWith(".json"));
|
|
544
575
|
const matches = [];
|
|
545
576
|
for (const file of files) {
|
|
546
|
-
const filePath =
|
|
547
|
-
const raw =
|
|
577
|
+
const filePath = path3.join(dataDir, file);
|
|
578
|
+
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
548
579
|
const hasMatch = keywords.some((kw) => raw.includes(kw));
|
|
549
580
|
if (hasMatch) {
|
|
550
581
|
matches.push(JSON.parse(raw));
|
|
@@ -572,11 +603,11 @@ var QueryHandler = class {
|
|
|
572
603
|
|
|
573
604
|
// src/web/server.ts
|
|
574
605
|
import http from "http";
|
|
575
|
-
import
|
|
576
|
-
import
|
|
606
|
+
import fs4 from "fs";
|
|
607
|
+
import path4 from "path";
|
|
577
608
|
import { fileURLToPath } from "url";
|
|
578
|
-
var __dirname =
|
|
579
|
-
var PUBLIC_DIR =
|
|
609
|
+
var __dirname = path4.dirname(fileURLToPath(import.meta.url));
|
|
610
|
+
var PUBLIC_DIR = path4.join(__dirname, "public");
|
|
580
611
|
var MIME_TYPES = {
|
|
581
612
|
".html": "text/html; charset=utf-8",
|
|
582
613
|
".css": "text/css; charset=utf-8",
|
|
@@ -586,30 +617,30 @@ var MIME_TYPES = {
|
|
|
586
617
|
".svg": "image/svg+xml"
|
|
587
618
|
};
|
|
588
619
|
function serveStatic(res, urlPath) {
|
|
589
|
-
const safePath =
|
|
590
|
-
let filePath =
|
|
591
|
-
if (!
|
|
592
|
-
filePath =
|
|
620
|
+
const safePath = path4.normalize(urlPath).replace(/^(\.\.[/\\])+/, "");
|
|
621
|
+
let filePath = path4.join(PUBLIC_DIR, safePath === "/" ? "index.html" : safePath);
|
|
622
|
+
if (!fs4.existsSync(filePath) || fs4.statSync(filePath).isDirectory()) {
|
|
623
|
+
filePath = path4.join(PUBLIC_DIR, "index.html");
|
|
593
624
|
}
|
|
594
|
-
const ext =
|
|
625
|
+
const ext = path4.extname(filePath);
|
|
595
626
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
596
|
-
const content =
|
|
627
|
+
const content = fs4.readFileSync(filePath);
|
|
597
628
|
res.writeHead(200, { "Content-Type": contentType });
|
|
598
629
|
res.end(content);
|
|
599
630
|
}
|
|
600
631
|
function listSessions(kmrDir) {
|
|
601
|
-
const sessionsDir =
|
|
602
|
-
if (!
|
|
603
|
-
return
|
|
604
|
-
const full =
|
|
605
|
-
return
|
|
632
|
+
const sessionsDir = path4.join(kmrDir, "sessions");
|
|
633
|
+
if (!fs4.existsSync(sessionsDir)) return [];
|
|
634
|
+
return fs4.readdirSync(sessionsDir).filter((d) => {
|
|
635
|
+
const full = path4.join(sessionsDir, d);
|
|
636
|
+
return fs4.statSync(full).isDirectory();
|
|
606
637
|
}).map((userId) => {
|
|
607
|
-
const summaryPath =
|
|
638
|
+
const summaryPath = path4.join(sessionsDir, userId, "summary.md");
|
|
608
639
|
let summaryPreview = "";
|
|
609
640
|
let messageCount = 0;
|
|
610
641
|
let lastActivity = "";
|
|
611
|
-
if (
|
|
612
|
-
const content =
|
|
642
|
+
if (fs4.existsSync(summaryPath)) {
|
|
643
|
+
const content = fs4.readFileSync(summaryPath, "utf-8");
|
|
613
644
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
614
645
|
messageCount = lines.length;
|
|
615
646
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
@@ -621,26 +652,26 @@ function listSessions(kmrDir) {
|
|
|
621
652
|
const lastLine = lines[lines.length - 1] || "";
|
|
622
653
|
const timeMatch = lastLine.match(/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]/);
|
|
623
654
|
if (timeMatch) lastActivity = timeMatch[1];
|
|
624
|
-
const stat =
|
|
655
|
+
const stat = fs4.statSync(summaryPath);
|
|
625
656
|
if (!lastActivity) lastActivity = stat.mtime.toISOString().replace("T", " ").slice(0, 19);
|
|
626
657
|
} else {
|
|
627
|
-
const userDir =
|
|
628
|
-
const stat =
|
|
658
|
+
const userDir = path4.join(sessionsDir, userId);
|
|
659
|
+
const stat = fs4.statSync(userDir);
|
|
629
660
|
lastActivity = stat.mtime.toISOString().replace("T", " ").slice(0, 19);
|
|
630
661
|
}
|
|
631
662
|
return { userId, summaryPreview, messageCount, lastActivity };
|
|
632
663
|
}).sort((a, b) => b.lastActivity.localeCompare(a.lastActivity));
|
|
633
664
|
}
|
|
634
665
|
function deleteSession(kmrDir, userId) {
|
|
635
|
-
const sessionDir =
|
|
636
|
-
if (!
|
|
637
|
-
|
|
666
|
+
const sessionDir = path4.join(kmrDir, "sessions", userId);
|
|
667
|
+
if (!fs4.existsSync(sessionDir)) return false;
|
|
668
|
+
fs4.rmSync(sessionDir, { recursive: true, force: true });
|
|
638
669
|
return true;
|
|
639
670
|
}
|
|
640
671
|
function getSessionDetail(kmrDir, userId) {
|
|
641
|
-
const summaryPath =
|
|
642
|
-
if (!
|
|
643
|
-
return
|
|
672
|
+
const summaryPath = path4.join(kmrDir, "sessions", userId, "summary.md");
|
|
673
|
+
if (!fs4.existsSync(summaryPath)) return null;
|
|
674
|
+
return fs4.readFileSync(summaryPath, "utf-8");
|
|
644
675
|
}
|
|
645
676
|
function startWebServer(store, kmrDir, port = 3e3) {
|
|
646
677
|
return new Promise((resolve, reject) => {
|
|
@@ -710,8 +741,8 @@ async function handleApi(req, res, url, store, kmrDir) {
|
|
|
710
741
|
return;
|
|
711
742
|
}
|
|
712
743
|
if (pathname === "/api/config" && method === "GET") {
|
|
713
|
-
const configPath =
|
|
714
|
-
const config = JSON.parse(
|
|
744
|
+
const configPath = path4.join(kmrDir, "config.json");
|
|
745
|
+
const config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
|
|
715
746
|
jsonResponse(res, 200, { ok: true, data: config });
|
|
716
747
|
return;
|
|
717
748
|
}
|
|
@@ -724,8 +755,8 @@ async function handleApi(req, res, url, store, kmrDir) {
|
|
|
724
755
|
jsonResponse(res, 400, { ok: false, error: "\u65E0\u6548\u7684 JSON \u683C\u5F0F" });
|
|
725
756
|
return;
|
|
726
757
|
}
|
|
727
|
-
const configPath =
|
|
728
|
-
|
|
758
|
+
const configPath = path4.join(kmrDir, "config.json");
|
|
759
|
+
fs4.writeFileSync(configPath, JSON.stringify(parsed, null, 2), "utf-8");
|
|
729
760
|
jsonResponse(res, 200, { ok: true, data: parsed });
|
|
730
761
|
return;
|
|
731
762
|
}
|
|
@@ -779,8 +810,8 @@ function openBrowser(url) {
|
|
|
779
810
|
// src/session/manager.ts
|
|
780
811
|
import { execFile as execFile3 } from "child_process";
|
|
781
812
|
import { promisify as promisify2 } from "util";
|
|
782
|
-
import
|
|
783
|
-
import
|
|
813
|
+
import fs5 from "fs";
|
|
814
|
+
import path5 from "path";
|
|
784
815
|
|
|
785
816
|
// src/session/skill.ts
|
|
786
817
|
var SESSION_SKILL = `\u4F60\u662F KMR\uFF08Key Meetings Record\uFF09\u7684\u667A\u80FD\u52A9\u624B\u3002
|
|
@@ -808,8 +839,8 @@ var execFileAsync2 = promisify2(execFile3);
|
|
|
808
839
|
var SessionManager = class {
|
|
809
840
|
constructor(kmrDir, timeout = 6e4) {
|
|
810
841
|
this.timeout = timeout;
|
|
811
|
-
this.sessionDir =
|
|
812
|
-
|
|
842
|
+
this.sessionDir = path5.join(kmrDir, "sessions");
|
|
843
|
+
fs5.mkdirSync(this.sessionDir, { recursive: true });
|
|
813
844
|
}
|
|
814
845
|
timeout;
|
|
815
846
|
activeSessions = /* @__PURE__ */ new Map();
|
|
@@ -828,15 +859,16 @@ var SessionManager = class {
|
|
|
828
859
|
return existing.name;
|
|
829
860
|
}
|
|
830
861
|
const sessionName = `kmr-${userId}`;
|
|
831
|
-
const userDir =
|
|
832
|
-
|
|
833
|
-
const skillPath =
|
|
834
|
-
|
|
862
|
+
const userDir = path5.join(this.sessionDir, userId);
|
|
863
|
+
fs5.mkdirSync(userDir, { recursive: true });
|
|
864
|
+
const skillPath = path5.join(userDir, "skill.md");
|
|
865
|
+
fs5.writeFileSync(skillPath, SESSION_SKILL, "utf-8");
|
|
835
866
|
console.log(`[session] \u521B\u5EFA\u65B0 session: ${sessionName}`);
|
|
836
867
|
try {
|
|
837
868
|
await execFileAsync2("acpx", ["claude", "sessions", "ensure", "--name", sessionName], {
|
|
838
869
|
timeout: this.timeout,
|
|
839
|
-
maxBuffer: 1024 * 1024
|
|
870
|
+
maxBuffer: 1024 * 1024,
|
|
871
|
+
env: getExecEnv()
|
|
840
872
|
});
|
|
841
873
|
} catch (err) {
|
|
842
874
|
console.error(`[session] \u521B\u5EFA session \u5931\u8D25:`, err.message);
|
|
@@ -847,7 +879,7 @@ var SessionManager = class {
|
|
|
847
879
|
await execFileAsync2(
|
|
848
880
|
"acpx",
|
|
849
881
|
["--approve-all", "claude", "-s", sessionName, SESSION_SKILL],
|
|
850
|
-
{ timeout: this.timeout, maxBuffer: 1024 * 1024 }
|
|
882
|
+
{ timeout: this.timeout, maxBuffer: 1024 * 1024, env: getExecEnv() }
|
|
851
883
|
);
|
|
852
884
|
} catch (err) {
|
|
853
885
|
console.error(`[session] \u89D2\u8272\u8BBE\u5B9A\u5931\u8D25:`, err.message);
|
|
@@ -863,7 +895,7 @@ var SessionManager = class {
|
|
|
863
895
|
const { stdout } = await execFileAsync2(
|
|
864
896
|
"acpx",
|
|
865
897
|
["--approve-all", "claude", "-s", sessionName, text],
|
|
866
|
-
{ timeout: this.timeout, maxBuffer: 1024 * 1024 }
|
|
898
|
+
{ timeout: this.timeout, maxBuffer: 1024 * 1024, env: getExecEnv() }
|
|
867
899
|
);
|
|
868
900
|
return this.extractReply(stdout);
|
|
869
901
|
} catch (err) {
|
|
@@ -882,21 +914,22 @@ var SessionManager = class {
|
|
|
882
914
|
return filtered || output.trim();
|
|
883
915
|
}
|
|
884
916
|
async appendSummary(userId, userText, aiReply) {
|
|
885
|
-
const userDir =
|
|
886
|
-
const summaryPath =
|
|
917
|
+
const userDir = path5.join(this.sessionDir, userId);
|
|
918
|
+
const summaryPath = path5.join(userDir, "summary.md");
|
|
887
919
|
const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
|
|
888
920
|
const entry = `
|
|
889
921
|
[${now}] \u7528\u6237: ${userText}
|
|
890
922
|
[${now}] AI: ${aiReply}
|
|
891
923
|
`;
|
|
892
|
-
|
|
924
|
+
fs5.appendFileSync(summaryPath, entry, "utf-8");
|
|
893
925
|
}
|
|
894
926
|
async closeSession(userId) {
|
|
895
927
|
const info = this.activeSessions.get(userId);
|
|
896
928
|
if (!info) return;
|
|
897
929
|
try {
|
|
898
930
|
await execFileAsync2("acpx", ["claude", "sessions", "close", info.name], {
|
|
899
|
-
timeout: 1e4
|
|
931
|
+
timeout: 1e4,
|
|
932
|
+
env: getExecEnv()
|
|
900
933
|
});
|
|
901
934
|
console.log(`[session] \u5DF2\u5173\u95ED session: ${info.name}`);
|
|
902
935
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lih-x-x/kmr",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"dev": "tsx src/index.ts",
|
|
18
|
-
"build": "tsup && cp -r src/web/public dist/public && node -e \"const fs=require('fs');const f='dist/cli.js';const c=fs.readFileSync(f,'utf8');if(!c.startsWith('#!')){fs.writeFileSync(f,'#!/usr/bin/env node\\n'+c)}\"",
|
|
18
|
+
"build": "tsup && rm -rf dist/public && cp -r src/web/public dist/public && node -e \"const fs=require('fs');const f='dist/cli.js';const c=fs.readFileSync(f,'utf8');if(!c.startsWith('#!')){fs.writeFileSync(f,'#!/usr/bin/env node\\n'+c)}\"",
|
|
19
19
|
"prepublishOnly": "npm run build",
|
|
20
20
|
"test": "vitest run",
|
|
21
21
|
"test:watch": "vitest",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|