@lih-x-x/kmr 1.0.60 → 1.0.62
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/README.md
CHANGED
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
## 功能
|
|
6
6
|
|
|
7
7
|
- **会议纪要提取**:发送飞书云文档链接给机器人,自动提取会议摘要、待办事项、风险项、关键共识、可复用知识
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
8
|
+
- **任务闭环**:提取待办后自动发送确认消息,回复 `/confirm` 创建飞书任务,`/reject` 取消,3 分钟超时自动取消。自动通过群成员列表解析负责人并指派任务,同步回写会议记录
|
|
9
|
+
- **本地 AI · 零成本接入**:基于 [acpx](https://www.npmjs.com/package/acpx) 复用本地 Claude / Codex,接入飞书机器人实现私有化部署,数据不出本机,无额外 API 费用
|
|
10
|
+
- **历史信息搜索**:会议记录和任务均可通过 AI 对话查询;`/find` 指令与自然语言双支持,返回最相关的 3 条结果
|
|
11
|
+
- **指令 / 自然语言双支持**:`/list`、`/show`、`/del`、`/find`、`/task` 等精确指令,与自由对话文本均可使用,AI 理解意图自动路由到对应操作
|
|
11
12
|
- **AI 自由对话**:发送非指令文本即可与 AI 自由对话,基于 acpx 持久化 session,按用户隔离,支持 claude 和 codex 两种 agent。AI 可直接调用 `kmr` 命令创建任务、查询记录,回复通过飞书机器人发送,处理中显示表情回应
|
|
12
13
|
- **独立任务创建**:通过 `kmr create-task` 命令(或 AI 对话)直接创建飞书任务,支持关联到指定会议记录(`--meeting <id>`),无关联时存入独立任务记录
|
|
13
|
-
-
|
|
14
|
+
- **首次启动自动引导**:无配置文件时自动初始化并打开 Web 设置页引导填写飞书凭证,配置完成后自动继续启动,无需手动 `kmr init`
|
|
14
15
|
- **记录管理**:`/list` 列出所有记录,`/show <id>` 查看详情,`/del <id>` 删除记录,`/task` 查看所有任务
|
|
15
16
|
- **CLI 命令**:`kmr list`、`kmr show`、`kmr task`、`kmr del`、`kmr find` 等子命令可在终端直接操作,无需启动服务
|
|
16
17
|
- **群聊智能识别**:群聊中非 @消息仅自动识别会议纪要文档链接(通过文档标题判断),其他操作需 @机器人
|
|
@@ -21,7 +22,7 @@
|
|
|
21
22
|
|
|
22
23
|
## 核心理念:不是记录员,而是分析师
|
|
23
24
|
|
|
24
|
-
KMR
|
|
25
|
+
KMR 不做流水账式的会议记录搬运。每条提取结果都围绕三个核心目标:
|
|
25
26
|
|
|
26
27
|
### 1. 溯源追责 — 关键共识与承诺
|
|
27
28
|
|
|
@@ -38,7 +39,13 @@ KMR 不做流水账式的会议记录搬运。每条提取结果都围绕两个
|
|
|
38
39
|
}
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
### 2.
|
|
42
|
+
### 2. 任务闭环 — 会议待办变真实任务
|
|
43
|
+
|
|
44
|
+
> 会议待办不再悬空,从纪要到飞书任务全程自动。
|
|
45
|
+
|
|
46
|
+
会议结束不是终点。KMR 提取待办后自动发起确认消息,回复 `/confirm` 即可在飞书创建任务并自动解析负责人,任务状态同步回写到会议记录——会开了,事就该定下来。
|
|
47
|
+
|
|
48
|
+
### 3. 知识复用 — 可提效的工具与方法
|
|
42
49
|
|
|
43
50
|
> 会上提到的工具用法、方法论、经验教训,下次用得上吗?
|
|
44
51
|
|
|
@@ -47,6 +47,15 @@ var UserResolver = class {
|
|
|
47
47
|
console.error(`[user-resolver] \u52A0\u8F7D\u7FA4\u6210\u5458\u5931\u8D25:`, err.message);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* 根据 open_id 反查姓名
|
|
52
|
+
*/
|
|
53
|
+
resolveName(openId) {
|
|
54
|
+
for (const [name, id] of this.nameMap) {
|
|
55
|
+
if (id === openId) return name;
|
|
56
|
+
}
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
50
59
|
/**
|
|
51
60
|
* 获取当前所有已缓存的映射
|
|
52
61
|
*/
|
package/dist/cli.js
CHANGED
|
@@ -6,9 +6,9 @@ async function checkUpdate() {
|
|
|
6
6
|
try {
|
|
7
7
|
const res = await fetch(`https://registry.npmjs.org/${"@lih-x-x/kmr"}/latest`, { signal: AbortSignal.timeout(3e3) });
|
|
8
8
|
const data = await res.json();
|
|
9
|
-
if (data.version && data.version !== "1.0.
|
|
9
|
+
if (data.version && data.version !== "1.0.62") {
|
|
10
10
|
console.log(`
|
|
11
|
-
\u2B06\uFE0F \u65B0\u7248\u672C\u53EF\u7528: ${"1.0.
|
|
11
|
+
\u2B06\uFE0F \u65B0\u7248\u672C\u53EF\u7528: ${"1.0.62"} \u2192 ${data.version}`);
|
|
12
12
|
console.log(` \u8FD0\u884C npm install -g ${"@lih-x-x/kmr"} \u66F4\u65B0
|
|
13
13
|
`);
|
|
14
14
|
}
|
|
@@ -56,7 +56,7 @@ KMR\uFF08Key Meetings Record\uFF09\u2014 \u4F1A\u8BAE\u6316\u6398\u673A
|
|
|
56
56
|
kmr --help \u663E\u793A\u5E2E\u52A9
|
|
57
57
|
`);
|
|
58
58
|
} else if (command === "--version" || command === "-v") {
|
|
59
|
-
console.log("1.0.
|
|
59
|
+
console.log("1.0.62");
|
|
60
60
|
} else if (command === "list") {
|
|
61
61
|
const { loadConfig } = await import("./config-WIQGW5OC.js");
|
|
62
62
|
const { JsonStore } = await import("./jsonStore-AL73KEUG.js");
|
|
@@ -163,7 +163,7 @@ KMR\uFF08Key Meetings Record\uFF09\u2014 \u4F1A\u8BAE\u6316\u6398\u673A
|
|
|
163
163
|
const { loadConfig } = await import("./config-WIQGW5OC.js");
|
|
164
164
|
const { createLarkClient } = await import("./client-DFBBDD77.js");
|
|
165
165
|
const { TaskCreator } = await import("./taskCreator-TCI3VB5D.js");
|
|
166
|
-
const { UserResolver } = await import("./userResolver-
|
|
166
|
+
const { UserResolver } = await import("./userResolver-SDRJIVT2.js");
|
|
167
167
|
const { JsonStore } = await import("./jsonStore-AL73KEUG.js");
|
|
168
168
|
const config = loadConfig();
|
|
169
169
|
const client = createLarkClient(config);
|
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
} from "./chunk-XQVLJTP4.js";
|
|
29
29
|
import {
|
|
30
30
|
UserResolver
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-23UVH7BV.js";
|
|
32
32
|
|
|
33
33
|
// src/index.ts
|
|
34
34
|
import fs3 from "fs";
|
|
@@ -590,6 +590,7 @@ var SESSION_SKILL = `\u4F60\u662F KMR\uFF08Key Meetings Record\uFF09\u7684\u667A
|
|
|
590
590
|
- \u4F60\u8FD0\u884C\u5728\u98DE\u4E66\u673A\u5668\u4EBA\u4E2D\uFF0C\u4F60\u7684\u56DE\u590D\u4F1A\u76F4\u63A5\u4F5C\u4E3A\u98DE\u4E66\u6D88\u606F\u53D1\u9001\u5230\u7528\u6237\u6240\u5728\u7684\u5BF9\u8BDD\u4E2D
|
|
591
591
|
- \u4E0D\u8981\u8BF4"\u6211\u6CA1\u6709\u53D1\u6D88\u606F/\u521B\u5EFA\u4EFB\u52A1\u7684\u80FD\u529B"\u2014\u2014\u4F60\u53EF\u4EE5\u901A\u8FC7 kmr \u547D\u4EE4\u6765\u5B8C\u6210\u8FD9\u4E9B\u64CD\u4F5C
|
|
592
592
|
- \u4F60\u6709\u80FD\u529B\u901A\u8FC7 kmr \u547D\u4EE4\u67E5\u8BE2\u6570\u636E\u3001\u521B\u5EFA\u4EFB\u52A1\u3001\u7BA1\u7406\u8BB0\u5F55
|
|
593
|
+
- \u652F\u6301\u7684 agent provider\uFF1Aclaude\u3001codex\u3001cursor\u3001kiro\u3001openclaw\u3001opencode\u3001gemini\uFF08\u7531\u914D\u7F6E\u51B3\u5B9A\uFF0C\u8FD0\u884C\u65F6\u4E0D\u53EF\u5207\u6362\uFF09
|
|
593
594
|
|
|
594
595
|
\u53EF\u7528\u7684 kmr \u547D\u4EE4\uFF08\u901A\u8FC7 bash \u6267\u884C\uFF09\uFF1A
|
|
595
596
|
- kmr list \u2014 \u5217\u51FA\u6240\u6709\u4F1A\u8BAE\u8BB0\u5F55
|
|
@@ -597,11 +598,12 @@ var SESSION_SKILL = `\u4F60\u662F KMR\uFF08Key Meetings Record\uFF09\u7684\u667A
|
|
|
597
598
|
- kmr find <query> \u2014 \u641C\u7D22\u4F1A\u8BAE\u8BB0\u5F55
|
|
598
599
|
- kmr task \u2014 \u67E5\u770B\u6240\u6709\u4EFB\u52A1
|
|
599
600
|
- kmr del <id> \u2014 \u5220\u9664\u8BB0\u5F55
|
|
600
|
-
- kmr create-task <\u4EFB\u52A1\u6807\u9898> [--due YYYY-MM-DD] [--assignee \u59D3\u540D] [--desc \u63CF\u8FF0] [--meeting <id>] \u2014 \u521B\u5EFA\u98DE\u4E66\u4EFB\u52A1\u5E76\u4FDD\u5B58\u5230\u672C\u5730\uFF08--meeting \u53EF\u5173\u8054\u5230\u6307\u5B9A\u4F1A\u8BAE\u8BB0\u5F55\uFF09
|
|
601
|
+
- kmr create-task <\u4EFB\u52A1\u6807\u9898> [--due YYYY-MM-DD] [--assignee \u59D3\u540D] [--desc \u63CF\u8FF0] [--meeting <id>] \u2014 \u521B\u5EFA\u98DE\u4E66\u4EFB\u52A1\u5E76\u4FDD\u5B58\u5230\u672C\u5730\uFF08--meeting \u53EF\u5173\u8054\u5230\u6307\u5B9A\u4F1A\u8BAE\u8BB0\u5F55\uFF0C\u82E5\u6709\uFF0C\u53EF\u9009\uFF09
|
|
601
602
|
|
|
602
603
|
\u64CD\u4F5C\u6307\u5357\uFF1A
|
|
603
604
|
- \u5F53\u7528\u6237\u8981\u6C42\u521B\u5EFA\u4EFB\u52A1\u65F6\uFF0C\u76F4\u63A5\u6267\u884C kmr create-task \u547D\u4EE4\uFF0C\u4F8B\u5982\uFF1Akmr create-task "\u5B8C\u6210\u65B9\u6848\u8BBE\u8BA1" --due 2026-05-15 --assignee \u5F20\u4E09\uFF1B\u5982\u679C\u80FD\u5173\u8054\u5230\u67D0\u6761\u4F1A\u8BAE\u8BB0\u5F55\uFF0C\u52A0\u4E0A --meeting <id>
|
|
604
605
|
- \u5F53\u7528\u6237\u95EE\u5230\u4F1A\u8BAE/\u4EFB\u52A1\u76F8\u5173\u7684\u95EE\u9898\u65F6\uFF0C\u5148\u6267\u884C kmr \u547D\u4EE4\u83B7\u53D6\u6570\u636E\u518D\u56DE\u7B54
|
|
606
|
+
- \u5F53\u7528\u6237\u95EE\u597D\u6216\u8005\u8BF7\u4F60\u4ECB\u7ECD\u65F6\uFF0C\u8981\u8F83\u4E3A\u8BE6\u7EC6\u5730\u4ECB\u7ECD\u81EA\u5DF1\u548C\u529F\u80FD\uFF0C\u5E2E\u52A9\u7528\u6237\u7406\u89E3\u4F60\u80FD\u505A\u4EC0\u4E48\u4EE5\u53CA\u4ED6\u80FD\u600E\u4E48\u4F7F\u7528
|
|
605
607
|
- \u6BCF\u6761\u6D88\u606F\u5F00\u5934\u4F1A\u6709\u3010\u5F53\u524D\u73AF\u5883\u3011\u63D0\u793A\uFF0C\u544A\u8BC9\u4F60\u662F\u7FA4\u804A\u8FD8\u662F\u79C1\u804A\uFF1A
|
|
606
608
|
- \u7FA4\u804A\uFF1A\u4F60\u7684\u56DE\u590D\u76F4\u63A5\u53D1\u5230\u7FA4\u91CC\uFF0C\u5982\u679C\u9700\u8981\u6307\u5B9A\u5BF9\u8C61\u5BF9\u8BDD\uFF0C\u53EF\u4EE5\u7528 @\u59D3\u540D \u6765\u63D0\u9192\u7FA4\u5185\u6210\u5458\uFF0C\u4E0D\u9700\u8981\u8BA9\u7528\u6237\u8F6C\u53D1
|
|
607
609
|
- \u79C1\u804A\uFF1A\u65E0\u6CD5 @\u4ED6\u4EBA\uFF0C\u5982\u9700\u901A\u77E5\u4ED6\u4EBA\u8BF7\u62DF\u5236\u6D88\u606F\u5185\u5BB9\u5EFA\u8BAE\u7528\u6237\u8F6C\u53D1
|
|
@@ -630,10 +632,10 @@ var SessionManager = class {
|
|
|
630
632
|
sessionDir;
|
|
631
633
|
async handleMessage(userId, text, context) {
|
|
632
634
|
const sessionName = await this.ensureSession(userId);
|
|
633
|
-
console.log(`[session] \u53D1\u9001\u6D88\u606F\u5230 session: ${sessionName}`);
|
|
634
635
|
let fullText = text;
|
|
635
636
|
if (context) {
|
|
636
|
-
const
|
|
637
|
+
const senderHint = context.senderName ? `\u53D1\u9001\u8005\uFF1A${context.senderName}\uFF08open_id: ${userId}\uFF09` : `\u53D1\u9001\u8005 open_id: ${userId}`;
|
|
638
|
+
const env = context.isGroup ? `\u3010\u5F53\u524D\u73AF\u5883\uFF1A\u7FA4\u804A\u3002${senderHint}\u3002\u4F60\u7684\u56DE\u590D\u4F1A\u53D1\u9001\u5230\u7FA4\u91CC\uFF0C\u5982\u679C\u9700\u8981\u6307\u5B9A\u5BF9\u8C61\u5BF9\u8BDD\uFF0C\u53EF\u4EE5\u76F4\u63A5\u7528 @\u59D3\u540D \u6765\u63D0\u9192\u7FA4\u5185\u6210\u5458\u3002\u3011` : `\u3010\u5F53\u524D\u73AF\u5883\uFF1A\u79C1\u804A\u3002${senderHint}\u3002\u65E0\u6CD5\u76F4\u63A5 @\u4ED6\u4EBA\uFF0C\u5982\u9700\u901A\u77E5\u4ED6\u4EBA\u8BF7\u62DF\u5236\u6D88\u606F\u5185\u5BB9\u8BA9\u7528\u6237\u8F6C\u53D1\u3002\u3011`;
|
|
637
639
|
fullText = `${env}
|
|
638
640
|
${text}`;
|
|
639
641
|
}
|
|
@@ -643,7 +645,8 @@ ${text}`;
|
|
|
643
645
|
}
|
|
644
646
|
getAgentCmd() {
|
|
645
647
|
const provider = getAgentProvider();
|
|
646
|
-
|
|
648
|
+
const supported = ["claude", "codex", "cursor", "kiro", "openclaw", "opencode", "gemini"];
|
|
649
|
+
return supported.includes(provider) ? provider : "claude";
|
|
647
650
|
}
|
|
648
651
|
/**
|
|
649
652
|
* 获取当前所有 claude-agent-acp 进程的 PID
|
|
@@ -686,7 +689,6 @@ ${text}`;
|
|
|
686
689
|
(line) => !line.includes("[closed]") && line.split(" ")[1]?.trim() === sessionName
|
|
687
690
|
);
|
|
688
691
|
if (hasExisting) {
|
|
689
|
-
console.log(`[session] \u53D1\u73B0\u6B8B\u7559 session: ${sessionName}\uFF0C\u5148\u5173\u95ED`);
|
|
690
692
|
try {
|
|
691
693
|
await execFileAsync("acpx", [agentCmd, "sessions", "close", sessionName], {
|
|
692
694
|
timeout: 1e4,
|
|
@@ -806,11 +808,9 @@ ${text}`;
|
|
|
806
808
|
async closeAll() {
|
|
807
809
|
const userIds = [...this.activeSessions.keys()];
|
|
808
810
|
if (userIds.length > 0) {
|
|
809
|
-
console.log(`[session] \u6B63\u5728\u5173\u95ED ${userIds.length} \u4E2A\u6D3B\u8DC3 session...`);
|
|
810
811
|
await Promise.all(userIds.map((uid) => this.closeSession(uid)));
|
|
811
812
|
}
|
|
812
813
|
if (this.ownedPids.size > 0) {
|
|
813
|
-
console.log(`[session] \u6B63\u5728\u6E05\u7406 ${this.ownedPids.size} \u4E2A agent \u8FDB\u7A0B...`);
|
|
814
814
|
for (const pid of this.ownedPids) {
|
|
815
815
|
try {
|
|
816
816
|
process.kill(pid, "SIGTERM");
|
|
@@ -874,7 +874,7 @@ async function main() {
|
|
|
874
874
|
const botOpenId = await getBotOpenId(client);
|
|
875
875
|
const dispatcher = createEventDispatcher(botOpenId, async (messageId, text, chatId, senderId, meta) => {
|
|
876
876
|
const parsed = parseMessage(text);
|
|
877
|
-
console.log(`[
|
|
877
|
+
console.log(`[message] \u6536\u5230\u6D88\u606F: "${text}" (chatId=${chatId}, senderId=${senderId})`);
|
|
878
878
|
if (meta.isGroup && !meta.isMentioned && parsed.type === "document_link" /* DOCUMENT_LINK */) {
|
|
879
879
|
const title = await docReader.getDocumentTitle(parsed.documentId);
|
|
880
880
|
const isMeetingNote = /纪要|会议记录|meeting/i.test(title) || /from=vc_assistant/.test(text);
|
|
@@ -1060,7 +1060,8 @@ async function main() {
|
|
|
1060
1060
|
}
|
|
1061
1061
|
console.log(`[process] \u81EA\u7531\u5BF9\u8BDD, userId=${senderId}`);
|
|
1062
1062
|
const reactionId = await messenger.addReaction(messageId, "OnIt");
|
|
1063
|
-
const
|
|
1063
|
+
const senderName = userResolver.resolveName(senderId);
|
|
1064
|
+
const reply = await sessionManager.handleMessage(senderId, text, { isGroup: meta.isGroup, senderName });
|
|
1064
1065
|
await messenger.removeReaction(messageId, reactionId);
|
|
1065
1066
|
await messenger.replyText(messageId, reply);
|
|
1066
1067
|
break;
|
package/dist/public/app.js
CHANGED
|
@@ -290,8 +290,9 @@ async function renderSettings() {
|
|
|
290
290
|
'<div class="form-field">' +
|
|
291
291
|
'<label class="form-label">Provider</label>' +
|
|
292
292
|
'<select class="form-select" id="cfg-provider">' +
|
|
293
|
-
'
|
|
294
|
-
|
|
293
|
+
['claude', 'codex', 'cursor', 'kiro', 'openclaw', 'opencode', 'gemini'].map(function(p) {
|
|
294
|
+
return '<option value="' + p + '"' + (config.agent.provider === p ? ' selected' : '') + '>' + p + '</option>';
|
|
295
|
+
}).join('') +
|
|
295
296
|
'</select>' +
|
|
296
297
|
'</div>' +
|
|
297
298
|
'<div class="form-field">' +
|