agentwake 1.0.0

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.
Files changed (91) hide show
  1. package/.cursor/hooks.json +15 -0
  2. package/.env.example +19 -0
  3. package/README.md +137 -0
  4. package/dist/adapters/claude-hook-adapter.d.ts +3 -0
  5. package/dist/adapters/claude-hook-adapter.d.ts.map +1 -0
  6. package/dist/adapters/claude-hook-adapter.js +25 -0
  7. package/dist/adapters/claude-hook-adapter.js.map +1 -0
  8. package/dist/adapters/cursor-hook-adapter.d.ts +3 -0
  9. package/dist/adapters/cursor-hook-adapter.d.ts.map +1 -0
  10. package/dist/adapters/cursor-hook-adapter.js +144 -0
  11. package/dist/adapters/cursor-hook-adapter.js.map +1 -0
  12. package/dist/adapters/cursor-terminal-hook.d.ts +20 -0
  13. package/dist/adapters/cursor-terminal-hook.d.ts.map +1 -0
  14. package/dist/adapters/cursor-terminal-hook.js +120 -0
  15. package/dist/adapters/cursor-terminal-hook.js.map +1 -0
  16. package/dist/adapters/hook-common.d.ts +6 -0
  17. package/dist/adapters/hook-common.d.ts.map +1 -0
  18. package/dist/adapters/hook-common.js +69 -0
  19. package/dist/adapters/hook-common.js.map +1 -0
  20. package/dist/adapters/qoder-log-adapter.d.ts +19 -0
  21. package/dist/adapters/qoder-log-adapter.d.ts.map +1 -0
  22. package/dist/adapters/qoder-log-adapter.js +320 -0
  23. package/dist/adapters/qoder-log-adapter.js.map +1 -0
  24. package/dist/bootstrap.d.ts +19 -0
  25. package/dist/bootstrap.d.ts.map +1 -0
  26. package/dist/bootstrap.js +106 -0
  27. package/dist/bootstrap.js.map +1 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +187 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/config.d.ts +21 -0
  33. package/dist/config.d.ts.map +1 -0
  34. package/dist/config.js +63 -0
  35. package/dist/config.js.map +1 -0
  36. package/dist/domain/notify-event.d.ts +17 -0
  37. package/dist/domain/notify-event.d.ts.map +1 -0
  38. package/dist/domain/notify-event.js +10 -0
  39. package/dist/domain/notify-event.js.map +1 -0
  40. package/dist/gateway/adapter-registry.d.ts +9 -0
  41. package/dist/gateway/adapter-registry.d.ts.map +1 -0
  42. package/dist/gateway/adapter-registry.js +26 -0
  43. package/dist/gateway/adapter-registry.js.map +1 -0
  44. package/dist/gateway/adapter.d.ts +14 -0
  45. package/dist/gateway/adapter.d.ts.map +1 -0
  46. package/dist/gateway/adapter.js +3 -0
  47. package/dist/gateway/adapter.js.map +1 -0
  48. package/dist/gateway/event-router.d.ts +20 -0
  49. package/dist/gateway/event-router.d.ts.map +1 -0
  50. package/dist/gateway/event-router.js +72 -0
  51. package/dist/gateway/event-router.js.map +1 -0
  52. package/dist/main.d.ts +2 -0
  53. package/dist/main.d.ts.map +1 -0
  54. package/dist/main.js +9 -0
  55. package/dist/main.js.map +1 -0
  56. package/dist/notifiers/desktop-notifier.d.ts +11 -0
  57. package/dist/notifiers/desktop-notifier.d.ts.map +1 -0
  58. package/dist/notifiers/desktop-notifier.js +78 -0
  59. package/dist/notifiers/desktop-notifier.js.map +1 -0
  60. package/dist/notifiers/mobile-ws-notifier.d.ts +13 -0
  61. package/dist/notifiers/mobile-ws-notifier.d.ts.map +1 -0
  62. package/dist/notifiers/mobile-ws-notifier.js +65 -0
  63. package/dist/notifiers/mobile-ws-notifier.js.map +1 -0
  64. package/dist/notifiers/notifier.d.ts +6 -0
  65. package/dist/notifiers/notifier.d.ts.map +1 -0
  66. package/dist/notifiers/notifier.js +3 -0
  67. package/dist/notifiers/notifier.js.map +1 -0
  68. package/dist/notifiers/pwa-push-notifier.d.ts +12 -0
  69. package/dist/notifiers/pwa-push-notifier.d.ts.map +1 -0
  70. package/dist/notifiers/pwa-push-notifier.js +69 -0
  71. package/dist/notifiers/pwa-push-notifier.js.map +1 -0
  72. package/dist/run-gateway.d.ts +3 -0
  73. package/dist/run-gateway.d.ts.map +1 -0
  74. package/dist/run-gateway.js +66 -0
  75. package/dist/run-gateway.js.map +1 -0
  76. package/dist/utils/approval-match.d.ts +2 -0
  77. package/dist/utils/approval-match.d.ts.map +1 -0
  78. package/dist/utils/approval-match.js +32 -0
  79. package/dist/utils/approval-match.js.map +1 -0
  80. package/dist/utils/logger.d.ts +7 -0
  81. package/dist/utils/logger.d.ts.map +1 -0
  82. package/dist/utils/logger.js +19 -0
  83. package/dist/utils/logger.js.map +1 -0
  84. package/package.json +60 -0
  85. package/scripts/cursor-hook-forwarder.mjs +269 -0
  86. package/web/app.js +350 -0
  87. package/web/icons/icon-192.svg +6 -0
  88. package/web/icons/icon-512.svg +6 -0
  89. package/web/index.html +117 -0
  90. package/web/manifest.webmanifest +25 -0
  91. package/web/sw.js +32 -0
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": 1,
3
+ "hooks": {
4
+ "beforeShellExecution": [
5
+ {
6
+ "command": "sh -lc 'CAROOT=\"$(mkcert -CAROOT 2>/dev/null)\"; EXTRA_CA=\"${NODE_EXTRA_CA_CERTS:-$CAROOT/rootCA.pem}\"; AGENTWAKE_GATEWAY_URL=\"${AGENTWAKE_GATEWAY_URL:-https://127.0.0.1:3199/hooks/cursor}\" NODE_EXTRA_CA_CERTS=\"$EXTRA_CA\" AGENTWAKE_CURSOR_APPROVAL_MODE=\"${AGENTWAKE_CURSOR_APPROVAL_MODE:-cursor-ask}\" AGENTWAKE_CURSOR_ENFORCE_ASK=\"${AGENTWAKE_CURSOR_ENFORCE_ASK:-0}\" node \"./scripts/cursor-hook-forwarder.mjs\"'"
7
+ }
8
+ ],
9
+ "afterShellExecution": [
10
+ {
11
+ "command": "sh -lc 'CAROOT=\"$(mkcert -CAROOT 2>/dev/null)\"; EXTRA_CA=\"${NODE_EXTRA_CA_CERTS:-$CAROOT/rootCA.pem}\"; AGENTWAKE_GATEWAY_URL=\"${AGENTWAKE_GATEWAY_URL:-https://127.0.0.1:3199/hooks/cursor}\" NODE_EXTRA_CA_CERTS=\"$EXTRA_CA\" AGENTWAKE_CURSOR_APPROVAL_MODE=\"${AGENTWAKE_CURSOR_APPROVAL_MODE:-cursor-ask}\" AGENTWAKE_CURSOR_ENFORCE_ASK=\"${AGENTWAKE_CURSOR_ENFORCE_ASK:-0}\" node \"./scripts/cursor-hook-forwarder.mjs\"'"
12
+ }
13
+ ]
14
+ }
15
+ }
package/.env.example ADDED
@@ -0,0 +1,19 @@
1
+ AGENTWAKE_HOST=0.0.0.0
2
+ AGENTWAKE_PORT=3199
3
+ AGENTWAKE_HTTPS_ENABLED=1
4
+ AGENTWAKE_HTTPS_CERT_PATH=certs/dev-cert.pem
5
+ AGENTWAKE_HTTPS_KEY_PATH=certs/dev-key.pem
6
+ AGENTWAKE_CURSOR_HOOK_PATH=/hooks/cursor
7
+ AGENTWAKE_CLAUDE_HOOK_PATH=/hooks/claude
8
+ AGENTWAKE_QODER_LOG_PATH=/path/to/qoder.log
9
+ AGENTWAKE_WS_PATH=/ws
10
+ AGENTWAKE_DEDUPE_WINDOW_MS=10000
11
+ AGENTWAKE_RATE_LIMIT_WINDOW_MS=10000
12
+ AGENTWAKE_RATE_LIMIT_MAX_EVENTS=40
13
+ AGENTWAKE_ALLOWED_HOOK_IPS=
14
+ AGENTWAKE_VAPID_SUBJECT=mailto:agentwake@example.com
15
+ AGENTWAKE_DESKTOP_MODE=notification
16
+ AGENTWAKE_CURSOR_APPROVAL_MODE=cursor-ask
17
+ AGENTWAKE_CURSOR_ENFORCE_ASK=0
18
+ # AGENTWAKE_VAPID_PUBLIC_KEY=
19
+ # AGENTWAKE_VAPID_PRIVATE_KEY=
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # AgentWake 🚀
2
+
3
+ **AgentWake** 是一个跨编辑器(Cursor / Claude Code / Qoder)的终端授权提醒网关。
4
+ 当你在使用 AI 辅助编程工具时,终端任务经常会进入“等待用户同意(Approval)”状态。AgentWake 会在桌面和移动端为你提供实时通知,让你无需时刻盯着屏幕,告别阻塞等待!
5
+
6
+ ---
7
+
8
+ ## 🌟 核心特性
9
+
10
+ - **多编辑器支持**:原生支持监听 Cursor Hook、Claude Hook 以及 Qoder 日志中的授权等待信号。
11
+ - **全平台桌面通知**:支持 macOS、Windows、Linux 的系统级弹窗提醒。
12
+ - **移动端实时提醒**:内置移动端 Web 应用,支持 PWA 安装,通过 HTTPS + WebSocket 实现手机端毫秒级推送。
13
+ - **智能防打扰**:内置去重和限流机制,避免消息重复轰炸。
14
+ - **开箱即用**:提供极简的 CLI 命令行工具,一键初始化与启动。
15
+
16
+ ---
17
+
18
+ ## 🚀 快速开始
19
+
20
+ ### 环境要求
21
+
22
+ - Node.js (推荐 v18+)
23
+ - [mkcert](https://github.com/FiloSottile/mkcert) (用于生成本地 HTTPS 证书,移动端访问必备)
24
+
25
+ ### 安装与启动
26
+
27
+ 1. **全局安装 CLI**
28
+ ```bash
29
+ npm i -g agentwake
30
+ ```
31
+
32
+ 2. **初始化项目与证书**
33
+ ```bash
34
+ mkdir my-agentwake && cd my-agentwake
35
+ agentwake init
36
+ ```
37
+ *注意:初始化过程中会自动使用 mkcert 生成本地 HTTPS 证书。*
38
+
39
+ 3. **启动网关服务**
40
+ ```bash
41
+ agentwake start
42
+ ```
43
+
44
+ 启动后,网关默认运行在 `https://localhost:3199` (或者局域网 IP)。
45
+
46
+ ---
47
+
48
+ ## 🔌 编辑器接入指南
49
+
50
+ ### Cursor 接入
51
+
52
+ AgentWake 已经内置了对 Cursor 工作流的支持:
53
+ 1. 确保在目标项目中执行过 `agentwake init`。
54
+ 2. 保持 `agentwake start` 运行。
55
+ 3. 当 Cursor 终端触发需要用户授权的命令时,你将立刻收到通知。
56
+
57
+ ### Qoder 接入
58
+
59
+ AgentWake 会尝试自动发现 Qoder 的日志目录(如 macOS 下的 `~/Library/Application Support/Qoder/logs/.../agent.log`)。
60
+
61
+ 如果自动发现失败,你可以通过环境变量手动指定日志路径:
62
+ ```bash
63
+ AGENTWAKE_QODER_LOG_PATH="/ABSOLUTE/PATH/TO/agent.log" agentwake start
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 📱 移动端访问与证书信任(必读)
69
+
70
+ 为了在手机上接收实时通知,你必须让手机信任电脑上由 `mkcert` 生成的根证书。
71
+
72
+ 1. 找到根证书位置:
73
+ 在电脑终端运行 `mkcert -CAROOT`,找到目录下的 `rootCA.pem` 文件。
74
+ 2. 安装到手机:
75
+ - **iOS**:将文件发送到手机,在“设置”中安装描述文件,并在“通用 -> 关于本机 -> 证书信任设置”中开启“完全信任”。
76
+ - **Android**:将文件发送到手机,在安全设置中“从存储设备安装” CA 证书(部分安卓系统可能需要将后缀改为 `.crt`)。
77
+ 3. Node.js TLS 兼容(如果遇到本地转发报错):
78
+ ```bash
79
+ export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem"
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 🛠️ 面向开发者
85
+
86
+ 如果你想参与 AgentWake 的开发,请参考以下指南:
87
+
88
+ ### 技术栈
89
+ - **后端**:Node.js + TypeScript + Express + WebSocket (`ws`) + Zod
90
+ - **前端**:HTML/CSS/JS (PWA 支持)
91
+ - **系统交互**:`node-notifier` (桌面通知)
92
+
93
+ ### 本地开发
94
+
95
+ ```bash
96
+ # 1. 克隆代码并安装依赖
97
+ git clone https://github.com/your-username/agentwake.git
98
+ cd agentwake
99
+ npm install
100
+
101
+ # 2. 准备环境变量
102
+ cp .env.example .env
103
+
104
+ # 3. 初始化(生成本地证书等)
105
+ npm run init
106
+
107
+ # 4. 启动开发服务器
108
+ npm run dev
109
+ ```
110
+
111
+ ### 核心目录结构
112
+ - `src/adapters/`:各类编辑器(Cursor/Claude/Qoder)的输入信号适配器。
113
+ - `src/gateway/`:核心网关,负责 Adapter 注册与事件路由。
114
+ - `src/notifiers/`:通知分发器(桌面系统通知、移动端 WebSocket、PWA Push)。
115
+ - `web/`:移动端 Web App 源码。
116
+
117
+ ---
118
+
119
+ ## ❓ 常见问题 (FAQ)
120
+
121
+ **Q: 为什么手机端收不到通知?**
122
+ A: 请按顺序检查:
123
+ 1. 手机是否和电脑在同一局域网下。
124
+ 2. 手机浏览器是否显示“安全/受信任”的 HTTPS 连接(若显示不安全,请重新检查证书安装步骤)。
125
+ 3. 检查 Web 页面上的 WebSocket 连接状态是否显示为“已连接”。
126
+
127
+ **Q: 能否自定义运行端口?**
128
+ A: 可以,通过 CLI 参数或环境变量修改:
129
+ ```bash
130
+ agentwake start --port 4000
131
+ # 或者使用环境变量
132
+ AGENTWAKE_PORT=4000 agentwake start
133
+ ```
134
+
135
+ ---
136
+
137
+ *AgentWake - 让 AI 编程更省心。*
@@ -0,0 +1,3 @@
1
+ import type { GatewayAdapter } from "../gateway/adapter";
2
+ export declare function createClaudeHookAdapter(): GatewayAdapter;
3
+ //# sourceMappingURL=claude-hook-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-hook-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/claude-hook-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,wBAAgB,uBAAuB,IAAI,cAAc,CAoBxD"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createClaudeHookAdapter = createClaudeHookAdapter;
4
+ const hook_common_1 = require("./hook-common");
5
+ function createClaudeHookAdapter() {
6
+ return {
7
+ id: "claude-hook-adapter",
8
+ start(context) {
9
+ context.app.post(context.config.claudeHookPath, async (req, res) => {
10
+ if (!(0, hook_common_1.validateHookSourceIp)(req, context.config.allowedHookIps)) {
11
+ (0, hook_common_1.forbidden)(res);
12
+ return;
13
+ }
14
+ const event = (0, hook_common_1.parseHookEvent)("claude-code", "claude-hook", req.body);
15
+ if (!event) {
16
+ res.status(202).json({ ok: true, accepted: false, reason: "ignored" });
17
+ return;
18
+ }
19
+ await context.emit(event);
20
+ res.status(200).json({ ok: true, accepted: true });
21
+ });
22
+ },
23
+ };
24
+ }
25
+ //# sourceMappingURL=claude-hook-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-hook-adapter.js","sourceRoot":"","sources":["../../src/adapters/claude-hook-adapter.ts"],"names":[],"mappings":";;AAOA,0DAoBC;AA1BD,+CAIuB;AAEvB,SAAgB,uBAAuB;IACrC,OAAO;QACL,EAAE,EAAE,qBAAqB;QACzB,KAAK,CAAC,OAAO;YACX,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;gBACjE,IAAI,CAAC,IAAA,kCAAoB,EAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;oBAC9D,IAAA,uBAAS,EAAC,GAAG,CAAC,CAAC;oBACf,OAAO;gBACT,CAAC;gBAED,MAAM,KAAK,GAAG,IAAA,4BAAc,EAAC,aAAa,EAAE,aAAa,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;gBACrE,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;oBACvE,OAAO;gBACT,CAAC;gBACD,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC1B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YACrD,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { GatewayAdapter } from "../gateway/adapter";
2
+ export declare function createCursorHookAdapter(): GatewayAdapter;
3
+ //# sourceMappingURL=cursor-hook-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-hook-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/cursor-hook-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAezD,wBAAgB,uBAAuB,IAAI,cAAc,CA0JxD"}
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCursorHookAdapter = createCursorHookAdapter;
4
+ const hook_common_1 = require("./hook-common");
5
+ const hook_common_2 = require("./hook-common");
6
+ const cursor_terminal_hook_1 = require("./cursor-terminal-hook");
7
+ const logger_1 = require("../utils/logger");
8
+ function createCursorHookAdapter() {
9
+ const pendingSignals = new Map();
10
+ const REQUEST_NOTIFY_DELAY_MS = 1_000;
11
+ const WAIT_THRESHOLD_MS = 2_000;
12
+ return {
13
+ id: "cursor-hook-adapter",
14
+ start(context) {
15
+ const cleanupSignal = (key) => {
16
+ const pending = pendingSignals.get(key);
17
+ if (!pending) {
18
+ return;
19
+ }
20
+ clearTimeout(pending.notifyTimer);
21
+ pendingSignals.delete(key);
22
+ };
23
+ const scheduleWaitingNotify = (key, signal) => {
24
+ cleanupSignal(key);
25
+ const requestedAtMs = Date.now();
26
+ const notifyTimer = setTimeout(() => {
27
+ const pending = pendingSignals.get(key);
28
+ if (!pending || pending.notified) {
29
+ return;
30
+ }
31
+ const waitMs = Date.now() - pending.requestedAtMs;
32
+ pending.notified = true;
33
+ void context
34
+ .emit((0, cursor_terminal_hook_1.createCursorApprovalEvent)({
35
+ signal: pending.signal,
36
+ reason: "time-gap-before-after",
37
+ waitMs,
38
+ }))
39
+ .catch((error) => logger_1.logger.error("cursor delayed notify failed", { error: String(error) }));
40
+ }, REQUEST_NOTIFY_DELAY_MS);
41
+ pendingSignals.set(key, {
42
+ signal,
43
+ requestedAtMs,
44
+ notifyTimer,
45
+ notified: false,
46
+ });
47
+ };
48
+ context.app.post(context.config.cursorHookPath, async (req, res) => {
49
+ logger_1.logger.info("cursor hook incoming", {
50
+ event: req.body?.hook_event_name,
51
+ command: req.body?.command,
52
+ permission: req.body?.permission,
53
+ requiresApproval: req.body?.requiresApproval,
54
+ ip: req.ip || req.socket.remoteAddress,
55
+ });
56
+ if (!(0, hook_common_1.validateHookSourceIp)(req, context.config.allowedHookIps)) {
57
+ (0, hook_common_1.forbidden)(res);
58
+ return;
59
+ }
60
+ const signal = (0, cursor_terminal_hook_1.parseCursorTerminalSignal)(req.body);
61
+ if (signal) {
62
+ const key = (0, cursor_terminal_hook_1.resolveCursorSignalKey)(signal);
63
+ if (signal.hookEvent === "beforeShellExecution") {
64
+ if (signal.explicitApproval) {
65
+ const event = (0, cursor_terminal_hook_1.createCursorApprovalEvent)({
66
+ signal,
67
+ reason: "explicit-hook-signal",
68
+ });
69
+ void context.emit(event).catch((error) => logger_1.logger.error("cursor notify emit failed", { error: String(error) }));
70
+ logger_1.logger.info("cursor hook accepted", {
71
+ dedupeKey: event.dedupeKey,
72
+ title: event.title,
73
+ level: event.level,
74
+ reason: "explicit-hook-signal",
75
+ });
76
+ res.status(200).json({ ok: true, accepted: true });
77
+ return;
78
+ }
79
+ scheduleWaitingNotify(key, signal);
80
+ logger_1.logger.info("cursor hook pending approval watch", {
81
+ key,
82
+ command: signal.command,
83
+ delayMs: REQUEST_NOTIFY_DELAY_MS,
84
+ });
85
+ res.status(202).json({ ok: true, accepted: false, reason: "pending-time-check" });
86
+ return;
87
+ }
88
+ const pending = pendingSignals.get(key);
89
+ if (pending) {
90
+ const waitMs = Math.max(typeof signal.durationMs === "number" ? signal.durationMs : 0, Date.now() - pending.requestedAtMs);
91
+ clearTimeout(pending.notifyTimer);
92
+ pendingSignals.delete(key);
93
+ if (!pending.notified && waitMs >= WAIT_THRESHOLD_MS) {
94
+ const event = (0, cursor_terminal_hook_1.createCursorApprovalEvent)({
95
+ signal: pending.signal,
96
+ reason: "time-gap-after-shell",
97
+ waitMs,
98
+ });
99
+ void context.emit(event).catch((error) => logger_1.logger.error("cursor notify emit failed", { error: String(error) }));
100
+ logger_1.logger.info("cursor hook accepted", {
101
+ dedupeKey: event.dedupeKey,
102
+ title: event.title,
103
+ level: event.level,
104
+ reason: "time-gap-after-shell",
105
+ waitMs,
106
+ });
107
+ res.status(200).json({ ok: true, accepted: true });
108
+ return;
109
+ }
110
+ logger_1.logger.info("cursor hook resolved quickly", {
111
+ key,
112
+ waitMs,
113
+ });
114
+ res.status(202).json({ ok: true, accepted: false, reason: "resolved-quickly" });
115
+ return;
116
+ }
117
+ }
118
+ const event = (0, cursor_terminal_hook_1.parseCursorTerminalHookEvent)(req.body) ?? (0, hook_common_2.parseHookEvent)("cursor", "cursor-hook", req.body);
119
+ if (!event) {
120
+ logger_1.logger.info("cursor hook ignored", {
121
+ event: req.body?.hook_event_name,
122
+ command: req.body?.command,
123
+ });
124
+ res.status(202).json({ ok: true, accepted: false, reason: "ignored" });
125
+ return;
126
+ }
127
+ logger_1.logger.info("cursor hook accepted", {
128
+ dedupeKey: event.dedupeKey,
129
+ title: event.title,
130
+ level: event.level,
131
+ });
132
+ await context.emit(event);
133
+ res.status(200).json({ ok: true, accepted: true });
134
+ });
135
+ return () => {
136
+ for (const pending of pendingSignals.values()) {
137
+ clearTimeout(pending.notifyTimer);
138
+ }
139
+ pendingSignals.clear();
140
+ };
141
+ },
142
+ };
143
+ }
144
+ //# sourceMappingURL=cursor-hook-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-hook-adapter.js","sourceRoot":"","sources":["../../src/adapters/cursor-hook-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type NotifyEvent } from "../domain/notify-event";
2
+ export type CursorTerminalSignal = {
3
+ hookEvent: "beforeShellExecution" | "afterShellExecution";
4
+ command: string;
5
+ cwd?: string;
6
+ conversationId?: string;
7
+ generationId?: string;
8
+ timestampMs: number;
9
+ explicitApproval: boolean;
10
+ durationMs?: number;
11
+ };
12
+ export declare function parseCursorTerminalSignal(body: unknown): CursorTerminalSignal | null;
13
+ export declare function resolveCursorSignalKey(signal: CursorTerminalSignal): string;
14
+ export declare function createCursorApprovalEvent(params: {
15
+ signal: CursorTerminalSignal;
16
+ reason?: string;
17
+ waitMs?: number;
18
+ }): NotifyEvent;
19
+ export declare function parseCursorTerminalHookEvent(body: unknown): NotifyEvent | null;
20
+ //# sourceMappingURL=cursor-terminal-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-terminal-hook.d.ts","sourceRoot":"","sources":["../../src/adapters/cursor-terminal-hook.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AA4B7E,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,sBAAsB,GAAG,qBAAqB,CAAC;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AA2BF,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,oBAAoB,GAAG,IAAI,CAwBpF;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAG3E;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE;IAChD,MAAM,EAAE,oBAAoB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,WAAW,CA+Bd;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAM9E"}
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseCursorTerminalSignal = parseCursorTerminalSignal;
4
+ exports.resolveCursorSignalKey = resolveCursorSignalKey;
5
+ exports.createCursorApprovalEvent = createCursorApprovalEvent;
6
+ exports.parseCursorTerminalHookEvent = parseCursorTerminalHookEvent;
7
+ const zod_1 = require("zod");
8
+ const notify_event_1 = require("../domain/notify-event");
9
+ const approval_match_1 = require("../utils/approval-match");
10
+ const cursorTerminalSchema = zod_1.z.object({
11
+ hook_event_name: zod_1.z.string().optional(),
12
+ conversation_id: zod_1.z.string().optional(),
13
+ generation_id: zod_1.z.string().optional(),
14
+ command: zod_1.z.string().optional(),
15
+ cwd: zod_1.z.string().optional(),
16
+ status: zod_1.z.string().optional(),
17
+ state: zod_1.z.string().optional(),
18
+ message: zod_1.z.string().optional(),
19
+ userMessage: zod_1.z.string().optional(),
20
+ agentMessage: zod_1.z.string().optional(),
21
+ reason: zod_1.z.string().optional(),
22
+ requiresApproval: zod_1.z.boolean().optional(),
23
+ approvalRequired: zod_1.z.boolean().optional(),
24
+ pendingApproval: zod_1.z.boolean().optional(),
25
+ permission: zod_1.z.string().optional(),
26
+ decision: zod_1.z.string().optional(),
27
+ continue: zod_1.z.boolean().optional(),
28
+ exit_code: zod_1.z.number().optional(),
29
+ duration: zod_1.z.number().optional(),
30
+ output: zod_1.z.string().optional(),
31
+ sandbox: zod_1.z.boolean().optional(),
32
+ workspace_roots: zod_1.z.array(zod_1.z.string()).optional(),
33
+ });
34
+ function compactBody(parts) {
35
+ return parts
36
+ .map((item) => item?.trim())
37
+ .filter((item) => Boolean(item))
38
+ .join("\n");
39
+ }
40
+ function resolveExplicitApproval(payload) {
41
+ return (payload.requiresApproval === true ||
42
+ payload.approvalRequired === true ||
43
+ payload.pendingApproval === true ||
44
+ payload.permission === "ask" ||
45
+ payload.decision === "ask" ||
46
+ payload.state === "awaiting_user_approval" ||
47
+ payload.state === "waiting_for_user_approval" ||
48
+ payload.status === "awaiting_user_approval" ||
49
+ payload.status === "waiting_for_user_approval" ||
50
+ (0, approval_match_1.isApprovalWaitingText)(payload.message ?? "") ||
51
+ (0, approval_match_1.isApprovalWaitingText)(payload.userMessage ?? "") ||
52
+ (0, approval_match_1.isApprovalWaitingText)(payload.agentMessage ?? "") ||
53
+ (0, approval_match_1.isApprovalWaitingText)(payload.reason ?? ""));
54
+ }
55
+ function parseCursorTerminalSignal(body) {
56
+ const parsed = cursorTerminalSchema.safeParse(body);
57
+ if (!parsed.success) {
58
+ return null;
59
+ }
60
+ const payload = parsed.data;
61
+ const hookEvent = payload.hook_event_name?.trim();
62
+ if (hookEvent !== "beforeShellExecution" && hookEvent !== "afterShellExecution") {
63
+ return null;
64
+ }
65
+ const command = payload.command?.trim();
66
+ if (!command) {
67
+ return null;
68
+ }
69
+ return {
70
+ hookEvent,
71
+ command,
72
+ ...(payload.cwd ? { cwd: payload.cwd } : {}),
73
+ ...(payload.conversation_id ? { conversationId: payload.conversation_id } : {}),
74
+ ...(payload.generation_id ? { generationId: payload.generation_id } : {}),
75
+ timestampMs: Date.now(),
76
+ explicitApproval: resolveExplicitApproval(payload),
77
+ ...(typeof payload.duration === "number" ? { durationMs: payload.duration } : {}),
78
+ };
79
+ }
80
+ function resolveCursorSignalKey(signal) {
81
+ const idPart = signal.generationId ?? signal.conversationId ?? "unknown";
82
+ return `${idPart}:${signal.command}`;
83
+ }
84
+ function createCursorApprovalEvent(params) {
85
+ const { signal } = params;
86
+ const waitSec = typeof params.waitMs === "number" && Number.isFinite(params.waitMs) ? params.waitMs / 1000 : undefined;
87
+ const waitToken = typeof waitSec === "number" ? `${waitSec.toFixed(1)}s` : undefined;
88
+ const bodyText = compactBody([
89
+ `命令: ${signal.command}`,
90
+ signal.cwd ? `目录: ${signal.cwd}` : undefined,
91
+ params.reason ? `原因: ${params.reason}` : undefined,
92
+ waitToken ? `等待时长: ${waitToken}` : undefined,
93
+ ]) || signal.command;
94
+ return (0, notify_event_1.createNotifyEvent)({
95
+ source: "cursor-hook",
96
+ editor: "cursor",
97
+ level: "warn",
98
+ title: "Cursor 终端等待用户同意",
99
+ body: bodyText,
100
+ dedupeKey: `cursor:approval:${resolveCursorSignalKey(signal)}`,
101
+ meta: {
102
+ hookEvent: signal.hookEvent,
103
+ command: signal.command,
104
+ cwd: signal.cwd,
105
+ conversationId: signal.conversationId,
106
+ generationId: signal.generationId,
107
+ reason: params.reason,
108
+ waitMs: params.waitMs,
109
+ explicitApproval: signal.explicitApproval,
110
+ },
111
+ });
112
+ }
113
+ function parseCursorTerminalHookEvent(body) {
114
+ const signal = parseCursorTerminalSignal(body);
115
+ if (!signal || signal.hookEvent !== "beforeShellExecution" || !signal.explicitApproval) {
116
+ return null;
117
+ }
118
+ return createCursorApprovalEvent({ signal, reason: "explicit-hook-signal" });
119
+ }
120
+ //# sourceMappingURL=cursor-terminal-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-terminal-hook.js","sourceRoot":"","sources":["../../src/adapters/cursor-terminal-hook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Request, Response } from "express";
2
+ import { type EditorKind, type NotifyEvent } from "../domain/notify-event";
3
+ export declare function parseHookEvent(editor: EditorKind, source: string, body: unknown): NotifyEvent | null;
4
+ export declare function validateHookSourceIp(req: Request, allowlist: string[]): boolean;
5
+ export declare function forbidden(res: Response): void;
6
+ //# sourceMappingURL=hook-common.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-common.d.ts","sourceRoot":"","sources":["../../src/adapters/hook-common.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEjD,OAAO,EAAqB,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAc9F,wBAAgB,cAAc,CAC5B,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,GACZ,WAAW,GAAG,IAAI,CAiCpB;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAgB/E;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAE7C"}
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseHookEvent = parseHookEvent;
4
+ exports.validateHookSourceIp = validateHookSourceIp;
5
+ exports.forbidden = forbidden;
6
+ const zod_1 = require("zod");
7
+ const notify_event_1 = require("../domain/notify-event");
8
+ const approval_match_1 = require("../utils/approval-match");
9
+ const hookPayloadSchema = zod_1.z.object({
10
+ title: zod_1.z.string().optional(),
11
+ message: zod_1.z.string().optional(),
12
+ text: zod_1.z.string().optional(),
13
+ event: zod_1.z.string().optional(),
14
+ status: zod_1.z.string().optional(),
15
+ requiresApproval: zod_1.z.boolean().optional(),
16
+ idempotencyKey: zod_1.z.string().optional(),
17
+ meta: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
18
+ });
19
+ function parseHookEvent(editor, source, body) {
20
+ const parsed = hookPayloadSchema.safeParse(body);
21
+ if (!parsed.success) {
22
+ return null;
23
+ }
24
+ const payload = parsed.data;
25
+ const title = payload.title?.trim() || `${editor} waiting`;
26
+ const message = payload.message?.trim() || payload.text?.trim() || "";
27
+ const status = payload.status?.trim() ?? "";
28
+ const evt = payload.event?.trim() ?? "";
29
+ const requiresApproval = payload.requiresApproval === true;
30
+ const hit = requiresApproval ||
31
+ (0, approval_match_1.isApprovalWaitingText)(message) ||
32
+ (0, approval_match_1.isApprovalWaitingText)(status) ||
33
+ (0, approval_match_1.isApprovalWaitingText)(evt);
34
+ if (!hit) {
35
+ return null;
36
+ }
37
+ const bodyText = message || status || evt || "Agent is waiting for user approval.";
38
+ const idempotencyKey = payload.idempotencyKey?.trim();
39
+ const dedupeKey = idempotencyKey || `${source}:${title}:${bodyText}`;
40
+ return (0, notify_event_1.createNotifyEvent)({
41
+ source,
42
+ editor,
43
+ level: "warn",
44
+ title,
45
+ body: bodyText,
46
+ dedupeKey,
47
+ ...(payload.meta ? { meta: payload.meta } : {}),
48
+ });
49
+ }
50
+ function validateHookSourceIp(req, allowlist) {
51
+ if (allowlist.length === 0) {
52
+ return true;
53
+ }
54
+ const raw = req.ip ||
55
+ req.socket.remoteAddress ||
56
+ (Array.isArray(req.headers["x-forwarded-for"])
57
+ ? req.headers["x-forwarded-for"][0]
58
+ : req.headers["x-forwarded-for"]) ||
59
+ "";
60
+ if (!raw) {
61
+ return false;
62
+ }
63
+ const ip = String(raw).split(",")[0]?.trim() ?? "";
64
+ return allowlist.includes(ip);
65
+ }
66
+ function forbidden(res) {
67
+ res.status(403).json({ ok: false, error: "forbidden source ip" });
68
+ }
69
+ //# sourceMappingURL=hook-common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-common.js","sourceRoot":"","sources":["../../src/adapters/hook-common.ts"],"names":[],"mappings":";;AAgBA,wCAqCC;AAED,oDAgBC;AAED,8BAEC;AA1ED,6BAAwB;AACxB,yDAA8F;AAC9F,4DAAgE;AAEhE,MAAM,iBAAiB,GAAG,OAAC,CAAC,MAAM,CAAC;IACjC,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,gBAAgB,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACxC,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACrC,IAAI,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;CACnD,CAAC,CAAC;AAEH,SAAgB,cAAc,CAC5B,MAAkB,EAClB,MAAc,EACd,IAAa;IAEb,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,UAAU,CAAC;IAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACtE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC5C,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxC,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,KAAK,IAAI,CAAC;IAC3D,MAAM,GAAG,GACP,gBAAgB;QAChB,IAAA,sCAAqB,EAAC,OAAO,CAAC;QAC9B,IAAA,sCAAqB,EAAC,MAAM,CAAC;QAC7B,IAAA,sCAAqB,EAAC,GAAG,CAAC,CAAC;IAE7B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,IAAI,MAAM,IAAI,GAAG,IAAI,qCAAqC,CAAC;IACnF,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;IACtD,MAAM,SAAS,GAAG,cAAc,IAAI,GAAG,MAAM,IAAI,KAAK,IAAI,QAAQ,EAAE,CAAC;IACrE,OAAO,IAAA,gCAAiB,EAAC;QACvB,MAAM;QACN,MAAM;QACN,KAAK,EAAE,MAAM;QACb,KAAK;QACL,IAAI,EAAE,QAAQ;QACd,SAAS;QACT,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChD,CAAC,CAAC;AACL,CAAC;AAED,SAAgB,oBAAoB,CAAC,GAAY,EAAE,SAAmB;IACpE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GACP,GAAG,CAAC,EAAE;QACN,GAAG,CAAC,MAAM,CAAC,aAAa;QACxB,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;YAC5C,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;YACnC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACnC,EAAE,CAAC;IACL,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACnD,OAAO,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,SAAgB,SAAS,CAAC,GAAa;IACrC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;AACpE,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { GatewayAdapter } from "../gateway/adapter";
2
+ export type QoderLogSignal = {
3
+ type: "permission_requested";
4
+ toolCallId: string;
5
+ toolName: string;
6
+ logTimestampSec: number | undefined;
7
+ } | {
8
+ type: "permission_resolved";
9
+ toolCallId: string;
10
+ outcome: "allow" | "reject" | "cancelled" | "unknown";
11
+ logTimestampSec: number | undefined;
12
+ } | {
13
+ type: "agent_suspended";
14
+ } | {
15
+ type: "agent_resumed";
16
+ };
17
+ export declare function parseQoderLogLine(line: string): QoderLogSignal | null;
18
+ export declare function createQoderLogAdapter(): GatewayAdapter;
19
+ //# sourceMappingURL=qoder-log-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qoder-log-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/qoder-log-adapter.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGtE,MAAM,MAAM,cAAc,GACtB;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC,GACD;IACE,IAAI,EAAE,qBAAqB,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;IACtD,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC,GACD;IACE,IAAI,EAAE,iBAAiB,CAAC;CACzB,GACD;IACE,IAAI,EAAE,eAAe,CAAC;CACvB,CAAC;AA2IN,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAyCrE;AAED,wBAAgB,qBAAqB,IAAI,cAAc,CA0KtD"}