lark-bridge-mcp 2.4.7 → 2.5.1
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 +381 -0
- package/dist/daemon-entry.d.ts +2 -0
- package/dist/daemon-entry.js +7 -0
- package/dist/daemon-entry.js.map +1 -0
- package/dist/daemon-scheduled-tasks.d.ts +3 -0
- package/dist/daemon-scheduled-tasks.js +221 -0
- package/dist/daemon-scheduled-tasks.js.map +1 -0
- package/dist/daemon.d.ts +1 -0
- package/dist/daemon.js +469 -0
- package/dist/daemon.js.map +1 -0
- package/dist/server.js +45 -297
- package/dist/server.js.map +1 -1
- package/dist/shared/lark-core.d.ts +43 -0
- package/dist/shared/lark-core.js +309 -0
- package/dist/shared/lark-core.js.map +1 -0
- package/package.json +16 -24
- package/src/file-queue.ts +0 -149
- package/src/index.ts +0 -11
- package/src/server.ts +0 -407
- package/tsconfig.json +0 -18
package/README.md
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# Feishu Cursor Bridge
|
|
2
|
+
|
|
3
|
+
飞书 × Cursor 远程协作桌面应用 —— 将 Cursor 变成 7×24 小时在线的数字雇员,通过飞书随时随地与 AI 协作。
|
|
4
|
+
|
|
5
|
+
## 为什么需要它?
|
|
6
|
+
|
|
7
|
+
Cursor Agent 的交互被锁死在本地 IDE 中,一旦离开电脑,所有 AI 协作都会停滞。
|
|
8
|
+
|
|
9
|
+
**Feishu Cursor Bridge** 打破了这种限制:
|
|
10
|
+
|
|
11
|
+
- AI 的提问会通过飞书机器人发到你手机上,你在飞书回复后 AI 自动继续工作
|
|
12
|
+
- 即使 Cursor 会话断开,守护进程也能自动重连拉起新会话
|
|
13
|
+
- 支持定时任务,让 AI 按计划自动执行工作
|
|
14
|
+
- 配合 Loop 协议,Cursor 计次版一次请求可持续交互一整天,500 次/月绰绰有余
|
|
15
|
+
|
|
16
|
+
## 功能特性
|
|
17
|
+
|
|
18
|
+
| 功能 | 说明 |
|
|
19
|
+
|------|------|
|
|
20
|
+
| 可视化配置 | 首次使用向导 + 设置页面,无需手写配置文件 |
|
|
21
|
+
| 一键启停 | 控制台一键管理守护进程生命周期 |
|
|
22
|
+
| 消息桥接 | AI 通过飞书发消息、发图片、发文件,你在飞书回复 |
|
|
23
|
+
| 自动重连 | Agent 断开后自动拉起新会话,最大程度保证连续性 |
|
|
24
|
+
| 指令系统 | 飞书发送 `/stop` `/status` `/restart` 等指令直接控制 |
|
|
25
|
+
| 定时任务 | Cron 表达式调度,定时给 AI 下达指令(如每天生成日报) |
|
|
26
|
+
| MCP 管理 | 可视化管理 MCP 服务器配置(JSON 编辑),支持 OAuth 认证 |
|
|
27
|
+
| Rule & Skill | 管理 Cursor Rules 和 Agent Skills 文件 |
|
|
28
|
+
| 系统托盘 | 关闭窗口自动最小化到托盘,后台持续运行 |
|
|
29
|
+
| 工作区注入 | 自动写入 `.cursor/mcp.json` 和 Loop 协议规则 |
|
|
30
|
+
|
|
31
|
+
## 轻量版 vs 应用版
|
|
32
|
+
|
|
33
|
+
| 维度 | 轻量版 | 应用版 |
|
|
34
|
+
|------|--------|--------|
|
|
35
|
+
| 形态 | MCP 服务(无 GUI) | Electron 桌面应用 |
|
|
36
|
+
| 自动重连 | 单次会话内无限循环 | Agent 断开后自动拉起新会话 |
|
|
37
|
+
| 定时任务 | 不支持 | Cron 表达式调度 |
|
|
38
|
+
| 配置管理 | 手动配置环境变量 + 规则文件 | 可视化设置界面 |
|
|
39
|
+
| 适用场景 | 快速上手 / 简单使用 / 节省次数 | 长期稳定运行 |
|
|
40
|
+
|
|
41
|
+
## 架构
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
┌────────────────────────────────────────────────────────────┐
|
|
45
|
+
│ Electron 桌面应用 │
|
|
46
|
+
│ · 配置向导 / 控制台 / 设置(React + Tailwind) │
|
|
47
|
+
│ · 管理 Daemon 生命周期、Cron 调度 │
|
|
48
|
+
│ · 自动注入 .cursor/mcp.json 和 Rules │
|
|
49
|
+
└──────────────┬──────────────────────────────┬──────────────┘
|
|
50
|
+
│ spawn │ 写入工作区
|
|
51
|
+
▼ ▼
|
|
52
|
+
┌──────────────────────────┐ ┌─────────────────────────────┐
|
|
53
|
+
│ Daemon 守护进程 │ │ .cursor/ │
|
|
54
|
+
│ · 飞书 WebSocket 长连接 │ │ ├── mcp.json → lite MCP │
|
|
55
|
+
│ · 本机 HTTP API │ │ └── rules/ │
|
|
56
|
+
│ · 消息队列 │ │ └── feishu-cursor-... │
|
|
57
|
+
│ · 会话保活(自动重连) │ └──────────────┬──────────────┘
|
|
58
|
+
└──────────────┬───────────┘ │ stdio
|
|
59
|
+
│ HTTP 127.0.0.1 ▼
|
|
60
|
+
│ ┌─────────────────────────────┐
|
|
61
|
+
└─────────────────►│ Lite MCP Server │
|
|
62
|
+
│ · sync_message(收发消息) │
|
|
63
|
+
│ · send_image(发送图片) │
|
|
64
|
+
│ · send_file(发送文件) │
|
|
65
|
+
│ Cursor 子进程,stdio 通信 │
|
|
66
|
+
└─────────────────────────────┘
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 安装
|
|
70
|
+
|
|
71
|
+
### 应用版(推荐)
|
|
72
|
+
|
|
73
|
+
从 [Releases](../../releases) 页面下载对应平台的安装包:
|
|
74
|
+
|
|
75
|
+
| 平台 | 格式 | 备注 |
|
|
76
|
+
|------|------|------|
|
|
77
|
+
| Windows | `.exe` | 直接运行安装 |
|
|
78
|
+
| macOS (Intel) | `.dmg` | 首次打开需解除 Gatekeeper |
|
|
79
|
+
| macOS (Apple Silicon) | `.dmg` | 首次打开需解除 Gatekeeper |
|
|
80
|
+
| macOS (Homebrew) | `brew install --cask` | 推荐,便于升级管理 |
|
|
81
|
+
| Linux | `.deb` / `.AppImage` | 直接运行 |
|
|
82
|
+
|
|
83
|
+
#### macOS 通过 Homebrew 安装
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
brew tap lk-eternal/tap
|
|
87
|
+
brew install --cask feishu-cursor-bridge
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
升级到最新版:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
brew update && brew upgrade --cask feishu-cursor-bridge
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### macOS 首次打开提示"无法验证开发者"
|
|
97
|
+
|
|
98
|
+
由于应用尚未经过 Apple 签名,macOS Gatekeeper 会拦截首次启动。解决方法:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
xattr -cr /Applications/Feishu\ Cursor\ Bridge.app
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
或到 **系统设置 → 隐私与安全性** 中点击"仍然打开"。
|
|
105
|
+
|
|
106
|
+
### 轻量版(纯 MCP,无 GUI)
|
|
107
|
+
|
|
108
|
+
#### 一键安装
|
|
109
|
+
|
|
110
|
+
[点击一键安装到 Cursor](https://cursor.com/en-US/install-mcp?name=feishu-cursor-bridge&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImxhcmstYnJpZGdlLW1jcEBsYXRlc3QiXSwiZW52Ijp7IkxBUktfQVBQX0lEIjoiIiwiTEFSS19BUFBfU0VDUkVUIjoiIiwiTEFSS19SRUNFSVZFX0lEX1RZUEUiOiJvcGVuX2lkIiwiTEFSS19SRUNFSVZFX0lEIjoiIn19),填入 App ID 和 App Secret 即可。
|
|
111
|
+
|
|
112
|
+
#### 手动安装
|
|
113
|
+
|
|
114
|
+
在 `.cursor/mcp.json` 中添加:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"mcpServers": {
|
|
119
|
+
"feishu-cursor-bridge": {
|
|
120
|
+
"command": "npx",
|
|
121
|
+
"args": ["-y", "lark-bridge-mcp@latest"],
|
|
122
|
+
"env": {
|
|
123
|
+
"LARK_APP_ID": "你的 App ID",
|
|
124
|
+
"LARK_APP_SECRET": "你的 App Secret"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
启动后在飞书找到你的机器人,私聊发一条消息。程序会自动记录你的 `open_id`。之后可以将 `open_id` 固定到配置中:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
"env": {
|
|
135
|
+
"LARK_APP_ID": "你的 App ID",
|
|
136
|
+
"LARK_APP_SECRET": "你的 App Secret",
|
|
137
|
+
"LARK_RECEIVE_ID": "ou_xxxxxxxxxxxxxx",
|
|
138
|
+
"LARK_RECEIVE_ID_TYPE": "open_id"
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 快速开始
|
|
143
|
+
|
|
144
|
+
### 应用版
|
|
145
|
+
|
|
146
|
+
1. 下载安装并启动应用
|
|
147
|
+
2. 按照向导填入飞书 App ID / App Secret
|
|
148
|
+
3. 配置消息接收者(支持自动识别、open_id、邮箱、手机号)
|
|
149
|
+
4. 选择 Cursor 工作目录,应用会自动注入 MCP 配置和 Loop 协议规则
|
|
150
|
+
5. 在 Dashboard 启动 Daemon,开始使用
|
|
151
|
+
|
|
152
|
+
> 如果选择"自动识别"方式,启动 Daemon 后需要在飞书私聊机器人发一条消息,系统会自动记住你的身份。
|
|
153
|
+
|
|
154
|
+
### 轻量版
|
|
155
|
+
|
|
156
|
+
1. 按照上方安装说明配置 MCP
|
|
157
|
+
2. 在 `.cursor/rules/` 目录下添加 Loop 协议规则文件(见下方 Loop 协议章节)
|
|
158
|
+
3. 在飞书私聊机器人发一条消息完成身份绑定
|
|
159
|
+
4. 在 Cursor 中开始使用
|
|
160
|
+
|
|
161
|
+
## Loop 协议:无限循环不计次
|
|
162
|
+
|
|
163
|
+
Loop 协议是实现「Agent 持续在线」的核心机制。它通过 Cursor Rules 强制 Agent 在任务完成后持续轮询飞书消息,而不是主动结束会话。
|
|
164
|
+
|
|
165
|
+
**Cursor 计次版用户的核心优势:** 一次 Agent 请求启动后,后续所有工具调用(包括 `sync_message` 的循环轮询)不消耗额外次数。配合 Loop 协议,一次请求可持续交互一整天。
|
|
166
|
+
|
|
167
|
+
> 应用版会自动注入 Loop 协议规则,无需手动配置。轻量版需要手动创建规则文件。
|
|
168
|
+
|
|
169
|
+
<details>
|
|
170
|
+
<summary>查看 Loop 协议规则文件(轻量版用户需手动添加)</summary>
|
|
171
|
+
|
|
172
|
+
在 `.cursor/rules/feishu-cursor-bridge.mdc` 中添加:
|
|
173
|
+
|
|
174
|
+
```yaml
|
|
175
|
+
---
|
|
176
|
+
description: 飞书实时通信保活协议
|
|
177
|
+
alwaysApply: true
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
# 飞书异步长连接执行协议 (Persistent Loop Protocol)
|
|
181
|
+
|
|
182
|
+
你当前处于"非对称异步通信模式"。在这种模式下,飞书是唯一的真理来源。
|
|
183
|
+
你必须通过 feishu-cursor-bridge MCP 工具主动维护会话状态,严禁本地空转或擅自结束。
|
|
184
|
+
|
|
185
|
+
## 状态机工作流
|
|
186
|
+
|
|
187
|
+
统一使用 `sync_message` 工具完成所有消息交互:
|
|
188
|
+
- 传 `message` → 发送消息
|
|
189
|
+
- 传 `timeout_seconds` → 等待用户回复
|
|
190
|
+
- 两者同时传 → 先发送再等待
|
|
191
|
+
|
|
192
|
+
1. **响应阶段 (Ack):** 收到用户指令后,立即发送确认消息。
|
|
193
|
+
2. **同步阶段 (Sync):** 每完成一个子步骤必须调用一次 sync_message 汇报进度。
|
|
194
|
+
3. **锚点阶段 (Anchor):** 任务完成时,发送结果并等待下一步指示。
|
|
195
|
+
4. **保活循环 (Keep-Alive):** 若返回超时或空值,持续调用 sync_message(timeout_seconds=60)。
|
|
196
|
+
|
|
197
|
+
## 核心禁令
|
|
198
|
+
|
|
199
|
+
* 严禁输出"再见"等导致会话终结的文本。
|
|
200
|
+
* 等待用户回复时,严禁重复发送相同消息。
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
</details>
|
|
204
|
+
|
|
205
|
+
## MCP 工具
|
|
206
|
+
|
|
207
|
+
| 工具 | 参数 | 说明 |
|
|
208
|
+
|------|------|------|
|
|
209
|
+
| `sync_message` | `message?`, `timeout_seconds?` | 发送消息到飞书 / 等待用户回复,或两者组合 |
|
|
210
|
+
| `send_image` | `path` | 发送本地图片到飞书 |
|
|
211
|
+
| `send_file` | `path` | 发送本地文件到飞书 |
|
|
212
|
+
|
|
213
|
+
**`sync_message` 用法示例:**
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
sync_message(message="方案A还是B?", timeout_seconds=60) → 发消息并等待回复
|
|
217
|
+
sync_message(timeout_seconds=60) → 仅轮询等待回复
|
|
218
|
+
sync_message(message="任务完成!") → 仅发送通知
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## 指令系统
|
|
222
|
+
|
|
223
|
+
在飞书对话中直接发送指令(不区分大小写),无需 Agent 运行:
|
|
224
|
+
|
|
225
|
+
| 指令 | 说明 |
|
|
226
|
+
|------|------|
|
|
227
|
+
| `/stop` | 停止运行中的 Agent |
|
|
228
|
+
| `/status` | 查看 Agent / Daemon 状态 |
|
|
229
|
+
| `/list` | 查看消息队列中的待处理消息 |
|
|
230
|
+
| `/task` | 查看当前定时任务列表 |
|
|
231
|
+
| `/model` | 查看/切换 Cursor CLI 模型(`ls` / `info` / `set <序号>`) |
|
|
232
|
+
| `/restart` | 停止 Agent → 清空队列 → 重启 Daemon |
|
|
233
|
+
| `/help` | 列出所有可用指令 |
|
|
234
|
+
|
|
235
|
+
## 会话保活与自动重连
|
|
236
|
+
|
|
237
|
+
Daemon 进程独立于 Cursor 运行,即使 Agent 会话中断,系统也能自动恢复:
|
|
238
|
+
|
|
239
|
+
1. **Daemon** 通过飞书 WebSocket 长连接持续监听消息,不依赖 Cursor 进程
|
|
240
|
+
2. **MCP Server** 每 15 秒向 Daemon 发送心跳,超时即判定 Agent 已断开
|
|
241
|
+
3. 当收到新的飞书消息且 Agent 已断开时,自动通过 Cursor CLI 拉起新会话
|
|
242
|
+
|
|
243
|
+
> 自动拉起需要安装 Cursor CLI(`agent` 命令)。应用版可在 Dashboard 一键安装。
|
|
244
|
+
|
|
245
|
+
## 环境变量(轻量版)
|
|
246
|
+
|
|
247
|
+
| 变量 | 必填 | 说明 |
|
|
248
|
+
|------|------|------|
|
|
249
|
+
| `LARK_APP_ID` | 是 | 飞书应用 App ID |
|
|
250
|
+
| `LARK_APP_SECRET` | 是 | 飞书应用 App Secret |
|
|
251
|
+
| `LARK_RECEIVE_ID` | 否 | 消息接收者标识(不填则自动从首条消息获取) |
|
|
252
|
+
| `LARK_RECEIVE_ID_TYPE` | 否 | ID 类型:`open_id` / `user_id` / `chat_id` / `email` / `mobile` |
|
|
253
|
+
| `LARK_ENCRYPT_KEY` | 否 | 事件加密密钥(长连接模式通常不需要) |
|
|
254
|
+
| `LARK_MESSAGE_PREFIX` | 否 | 发送消息前缀 |
|
|
255
|
+
|
|
256
|
+
## 飞书应用配置
|
|
257
|
+
|
|
258
|
+
1. 前往 [飞书开放平台](https://open.feishu.cn/app/) 创建自建应用
|
|
259
|
+
2. 获取 App ID 和 App Secret
|
|
260
|
+
3. 添加「机器人」能力
|
|
261
|
+
4. 在「权限管理」中开通权限(可通过批量导入快速添加):
|
|
262
|
+
- `im:message` — 获取与发送单聊、群组消息
|
|
263
|
+
- `im:message.p2p_msg:readonly` — 读取用户发给机器人的单聊消息
|
|
264
|
+
- `im:resource` — 获取与上传图片或文件资源
|
|
265
|
+
- `contact:user.id:readonly` — 通过邮箱/手机号查找用户(可选)
|
|
266
|
+
|
|
267
|
+
<details>
|
|
268
|
+
<summary>批量导入权限 JSON</summary>
|
|
269
|
+
|
|
270
|
+
```json
|
|
271
|
+
{
|
|
272
|
+
"scopes": {
|
|
273
|
+
"tenant": [
|
|
274
|
+
"contact:user.id:readonly",
|
|
275
|
+
"im:message",
|
|
276
|
+
"im:message.p2p_msg:readonly",
|
|
277
|
+
"im:resource"
|
|
278
|
+
],
|
|
279
|
+
"user": []
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
</details>
|
|
285
|
+
|
|
286
|
+
5. 在「事件与回调」中选择 **「长连接」** 模式,添加 `im.message.receive_v1` 事件
|
|
287
|
+
|
|
288
|
+
> **注意:** 配置事件订阅前需先启动服务(MCP 或应用版),否则飞书无法验证 WebSocket 连接。
|
|
289
|
+
|
|
290
|
+
6. 在「版本管理与发布」中发布应用
|
|
291
|
+
7. 在飞书私聊机器人发一条消息完成身份绑定
|
|
292
|
+
|
|
293
|
+
## 飞书全链路研发自动化
|
|
294
|
+
|
|
295
|
+
配合以下 MCP 服务,可实现从需求分析到代码交付的全链路自动化:
|
|
296
|
+
|
|
297
|
+
- **飞书文档 MCP**:读取 PRD、自动撰写技术方案、同步变更说明
|
|
298
|
+
- 配置入口:[https://open.feishu.cn/page/mcp](https://open.feishu.cn/page/mcp)
|
|
299
|
+
- **飞书项目 MCP**:获取待办任务、更新工作项状态、生成进度报告
|
|
300
|
+
- 配置:`"feishu-project-mcp": { "url": "https://project.feishu.cn/mcp_server/v1" }`
|
|
301
|
+
|
|
302
|
+
## 常见问题
|
|
303
|
+
|
|
304
|
+
<details>
|
|
305
|
+
<summary>Agent 会话为什么会断开?</summary>
|
|
306
|
+
|
|
307
|
+
常见原因:
|
|
308
|
+
- **上下文窗口超限**:超长会话会被自动截断,建议复杂任务拆分或使用 `.cursor/memory.md` 持久化关键信息
|
|
309
|
+
- **工具调用过多**:单次会话中工具调用次数过多可能触发 Cursor 安全机制
|
|
310
|
+
- **网络波动**:本地网络不稳定可能导致 MCP stdio 通信中断
|
|
311
|
+
- **Cursor 更新/重启**:IDE 自动更新会中断当前会话
|
|
312
|
+
|
|
313
|
+
> 应用版可在 Agent 断开后自动重新拉起会话。
|
|
314
|
+
|
|
315
|
+
</details>
|
|
316
|
+
|
|
317
|
+
<details>
|
|
318
|
+
<summary>为什么飞书收不到消息?</summary>
|
|
319
|
+
|
|
320
|
+
请按顺序排查:
|
|
321
|
+
1. 确认添加了 `im.message.receive_v1` 事件订阅,且选择「长连接」模式
|
|
322
|
+
2. 确认应用已发布(未发布的应用无法接收消息)
|
|
323
|
+
3. 确认 `im:message` 和 `im:message.p2p_msg:readonly` 权限已添加
|
|
324
|
+
4. 确认服务已启动且飞书 WebSocket 连接成功
|
|
325
|
+
5. 确认是在机器人私聊窗口发送消息
|
|
326
|
+
|
|
327
|
+
</details>
|
|
328
|
+
|
|
329
|
+
<details>
|
|
330
|
+
<summary>无限循环真的不计次吗?</summary>
|
|
331
|
+
|
|
332
|
+
是的。在 Cursor 计次版(Fast Request 模式)下,一次 Agent 请求启动后,后续所有工具调用不消耗额外次数。配合 Loop 协议,Agent 会在完成任务后持续等待新指令,整个生命周期只算一次请求。
|
|
333
|
+
|
|
334
|
+
</details>
|
|
335
|
+
|
|
336
|
+
<details>
|
|
337
|
+
<summary>定时任务需要电脑一直开着吗?</summary>
|
|
338
|
+
|
|
339
|
+
是的。定时任务由应用版调度,需要桌面应用保持运行。关闭窗口后应用会最小化到系统托盘继续运行,但完全退出或关机后定时任务将不会触发。
|
|
340
|
+
|
|
341
|
+
</details>
|
|
342
|
+
|
|
343
|
+
## 注意事项
|
|
344
|
+
|
|
345
|
+
- **会话重连是新上下文**:重新拉起的 Agent 会话没有之前的对话记忆,重要状态应通过 `.cursor/memory.md` 持久化
|
|
346
|
+
- **凭据安全**:App Secret 是敏感信息,请勿提交到 Git 仓库
|
|
347
|
+
- **网络要求**:Daemon 需保持与飞书服务器的网络连接,企业网络如有代理限制,可在设置中配置代理
|
|
348
|
+
- **Cursor CLI 依赖**:自动拉起 Agent 功能依赖 Cursor CLI,需在 Dashboard 中安装
|
|
349
|
+
|
|
350
|
+
## 开发
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
# 安装依赖
|
|
354
|
+
npm install
|
|
355
|
+
cd lite && npm install && cd ..
|
|
356
|
+
|
|
357
|
+
# 开发模式
|
|
358
|
+
npm run dev
|
|
359
|
+
|
|
360
|
+
# 构建
|
|
361
|
+
npm run build
|
|
362
|
+
|
|
363
|
+
# 打包
|
|
364
|
+
npm run dist:win # Windows
|
|
365
|
+
npm run dist:mac # macOS
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## License
|
|
369
|
+
|
|
370
|
+
MIT
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
## Star History
|
|
374
|
+
|
|
375
|
+
<a href="https://www.star-history.com/?repos=lk-eternal%2Ffeishu-cursor-bridge&type=date&legend=top-left">
|
|
376
|
+
<picture>
|
|
377
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/image?repos=lk-eternal/feishu-cursor-bridge&type=date&theme=dark&legend=top-left" />
|
|
378
|
+
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/image?repos=lk-eternal/feishu-cursor-bridge&type=date&legend=top-left" />
|
|
379
|
+
<img alt="Star History Chart" src="https://api.star-history.com/image?repos=lk-eternal/feishu-cursor-bridge&type=date&legend=top-left" />
|
|
380
|
+
</picture>
|
|
381
|
+
</a>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-entry.js","sourceRoot":"","sources":["../src/daemon-entry.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function setDaemonSchedulerLogger(fn: (msg: string) => void): void;
|
|
2
|
+
export declare function startDaemonScheduledTasks(enqueue: (content: string) => void, launchIndependent?: (taskId: string, taskName: string, content: string) => void): void;
|
|
3
|
+
export declare function stopDaemonScheduledTasks(): void;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { CronExpressionParser } from "cron-parser";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import * as os from "node:os";
|
|
5
|
+
const TASKS_DIR = path.join(os.homedir(), ".lark-bridge-mcp");
|
|
6
|
+
const TASKS_FILE = path.join(TASKS_DIR, "scheduled-tasks.json");
|
|
7
|
+
/** 轮询间隔:不依赖单次 setTimeout 链,避免锁屏/会话节流导致整点永不触发 */
|
|
8
|
+
const WATCHDOG_MS = 5_000;
|
|
9
|
+
/** 仅接受计划触发时刻距今不超过此时长(短时卡顿/锁屏补救);睡眠过久唤醒后不补跑过期槽位 */
|
|
10
|
+
const CATCHUP_MAX_MS = 30 * 60 * 1000;
|
|
11
|
+
/** 单任务单次 tick 内最多向前迭代次数 */
|
|
12
|
+
const MAX_FIRES_PER_TICK = 10_000;
|
|
13
|
+
let scheduledTasksSnapshot = [];
|
|
14
|
+
let watchdogTimer = null;
|
|
15
|
+
let lastWatchdogMs = 0;
|
|
16
|
+
const firedSlotKeys = new Set();
|
|
17
|
+
let fileWatcher = null;
|
|
18
|
+
let logFn = null;
|
|
19
|
+
export function setDaemonSchedulerLogger(fn) {
|
|
20
|
+
logFn = fn;
|
|
21
|
+
}
|
|
22
|
+
function log(msg) {
|
|
23
|
+
if (logFn) {
|
|
24
|
+
logFn(`[定时任务] ${msg}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function readTasksFromFile() {
|
|
28
|
+
try {
|
|
29
|
+
if (!fs.existsSync(TASKS_FILE)) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const raw = fs.readFileSync(TASKS_FILE, "utf-8");
|
|
33
|
+
const parsed = JSON.parse(raw);
|
|
34
|
+
if (!Array.isArray(parsed)) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
return parsed.filter((t) => typeof t === "object" && t !== null &&
|
|
38
|
+
typeof t.id === "string" &&
|
|
39
|
+
typeof t.name === "string" &&
|
|
40
|
+
typeof t.cron === "string" &&
|
|
41
|
+
typeof t.content === "string").map((t) => ({ ...t, enabled: t.enabled !== false }));
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
log(`读取任务文件失败: ${e instanceof Error ? e.message : String(e)}`);
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function isValidCron(expression) {
|
|
49
|
+
const trimmed = expression.trim();
|
|
50
|
+
if (!trimmed) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
CronExpressionParser.parse(trimmed, { currentDate: new Date() });
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function collectDueCronFires(expression, rangeStartExclusive, rangeEndInclusive) {
|
|
62
|
+
const out = [];
|
|
63
|
+
const startMs = rangeStartExclusive.getTime();
|
|
64
|
+
const endMs = rangeEndInclusive.getTime();
|
|
65
|
+
if (endMs <= startMs) {
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
let cursor = new Date(startMs + 1);
|
|
69
|
+
for (let i = 0; i < MAX_FIRES_PER_TICK; i++) {
|
|
70
|
+
let interval;
|
|
71
|
+
try {
|
|
72
|
+
interval = CronExpressionParser.parse(expression, { currentDate: cursor });
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
const next = interval.next().toDate();
|
|
78
|
+
const nt = next.getTime();
|
|
79
|
+
if (nt > endMs) {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
if (nt > startMs) {
|
|
83
|
+
out.push(next);
|
|
84
|
+
}
|
|
85
|
+
cursor = new Date(nt + 1000);
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
function stopWatchdog() {
|
|
90
|
+
if (watchdogTimer) {
|
|
91
|
+
clearInterval(watchdogTimer);
|
|
92
|
+
watchdogTimer = null;
|
|
93
|
+
}
|
|
94
|
+
lastWatchdogMs = 0;
|
|
95
|
+
}
|
|
96
|
+
/** 按时间窗口扫描应触发点;仅接受距今不超过 CATCHUP_MAX_MS 的槽位(过时即丢弃,避免睡眠唤醒后执行已失效任务)。 */
|
|
97
|
+
function runWatchdogTick(cb) {
|
|
98
|
+
if (scheduledTasksSnapshot.length === 0) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const nowMs = Date.now();
|
|
102
|
+
const prevMs = lastWatchdogMs === 0 ? nowMs - WATCHDOG_MS : lastWatchdogMs;
|
|
103
|
+
const rangeStartExclusive = new Date(prevMs);
|
|
104
|
+
const rangeEndInclusive = new Date(nowMs);
|
|
105
|
+
lastWatchdogMs = nowMs;
|
|
106
|
+
for (const task of scheduledTasksSnapshot) {
|
|
107
|
+
if (!task.enabled) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const expr = task.cron.trim();
|
|
111
|
+
let fires;
|
|
112
|
+
try {
|
|
113
|
+
fires = collectDueCronFires(expr, rangeStartExclusive, rangeEndInclusive);
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
log(`解析 cron 失败: ${task.name} — ${e instanceof Error ? e.message : String(e)}`);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
for (const fireAt of fires) {
|
|
120
|
+
if (nowMs - fireAt.getTime() > CATCHUP_MAX_MS) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const slotKey = `${task.id}:${fireAt.getTime()}`;
|
|
124
|
+
if (firedSlotKeys.has(slotKey)) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
firedSlotKeys.add(slotKey);
|
|
128
|
+
if (firedSlotKeys.size > 2_000) {
|
|
129
|
+
firedSlotKeys.clear();
|
|
130
|
+
}
|
|
131
|
+
const nowStr = fireAt.toLocaleString("zh-CN");
|
|
132
|
+
const message = `[定时任务: ${task.name}] (触发时间: ${nowStr})\n\n${task.content}`;
|
|
133
|
+
log(`触发: ${task.name}${task.independent ? " [独立运行]" : ""}`);
|
|
134
|
+
if (task.independent && cb.launchIndependent) {
|
|
135
|
+
cb.launchIndependent(task.id, task.name, message);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
cb.enqueue(message);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function reloadTasks(cb) {
|
|
144
|
+
stopWatchdog();
|
|
145
|
+
scheduledTasksSnapshot = [];
|
|
146
|
+
const tasks = readTasksFromFile();
|
|
147
|
+
const enabled = tasks.filter((t) => t.enabled);
|
|
148
|
+
if (enabled.length === 0) {
|
|
149
|
+
log(`无活跃定时任务 (共 ${tasks.length} 个)`);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
log(`加载 ${enabled.length} 个定时任务`);
|
|
153
|
+
for (const task of enabled) {
|
|
154
|
+
if (!isValidCron(task.cron)) {
|
|
155
|
+
log(`无效的 cron 表达式: "${task.cron}" (任务: ${task.name})`);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
scheduledTasksSnapshot.push(task);
|
|
159
|
+
log(`已注册: ${task.name} (${task.cron})${task.independent ? " [独立]" : ""}`);
|
|
160
|
+
}
|
|
161
|
+
if (scheduledTasksSnapshot.length === 0) {
|
|
162
|
+
log("无有效定时任务(表达式均无效)");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
lastWatchdogMs = 0;
|
|
166
|
+
watchdogTimer = setInterval(() => {
|
|
167
|
+
runWatchdogTick(cb);
|
|
168
|
+
}, WATCHDOG_MS);
|
|
169
|
+
runWatchdogTick(cb);
|
|
170
|
+
}
|
|
171
|
+
function stopFileWatcher() {
|
|
172
|
+
if (fileWatcher) {
|
|
173
|
+
fileWatcher.close();
|
|
174
|
+
fileWatcher = null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function startFileWatcher(cb) {
|
|
178
|
+
stopFileWatcher();
|
|
179
|
+
if (!fs.existsSync(TASKS_DIR)) {
|
|
180
|
+
try {
|
|
181
|
+
fs.mkdirSync(TASKS_DIR, { recursive: true });
|
|
182
|
+
}
|
|
183
|
+
catch { /* ignore */ }
|
|
184
|
+
}
|
|
185
|
+
let debounceTimer = null;
|
|
186
|
+
try {
|
|
187
|
+
fileWatcher = fs.watch(TASKS_DIR, (_eventType, filename) => {
|
|
188
|
+
if (filename !== "scheduled-tasks.json") {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (debounceTimer) {
|
|
192
|
+
clearTimeout(debounceTimer);
|
|
193
|
+
}
|
|
194
|
+
debounceTimer = setTimeout(() => {
|
|
195
|
+
log("检测到定时任务配置文件变化,重新加载...");
|
|
196
|
+
reloadTasks(cb);
|
|
197
|
+
}, 500);
|
|
198
|
+
});
|
|
199
|
+
fileWatcher.on("error", () => { });
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
202
|
+
log(`文件监听启动失败: ${e instanceof Error ? e.message : String(e)}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export function startDaemonScheduledTasks(enqueue, launchIndependent) {
|
|
206
|
+
const cb = { enqueue, launchIndependent };
|
|
207
|
+
reloadTasks(cb);
|
|
208
|
+
startFileWatcher(cb);
|
|
209
|
+
log(`调度器已启动 (${scheduledTasksSnapshot.length} 个活跃任务,每 ${WATCHDOG_MS / 1000}s 时钟扫描)`);
|
|
210
|
+
}
|
|
211
|
+
export function stopDaemonScheduledTasks() {
|
|
212
|
+
const count = scheduledTasksSnapshot.length;
|
|
213
|
+
stopWatchdog();
|
|
214
|
+
stopFileWatcher();
|
|
215
|
+
scheduledTasksSnapshot = [];
|
|
216
|
+
firedSlotKeys.clear();
|
|
217
|
+
if (count > 0) {
|
|
218
|
+
log(`调度器已停止 (${count} 个任务已停止)`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=daemon-scheduled-tasks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-scheduled-tasks.js","sourceRoot":"","sources":["../src/daemon-scheduled-tasks.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function daemonMain(): Promise<void>;
|