@ulthon/ul-opencode-event 0.1.27 → 0.1.30

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
@@ -1,431 +1,147 @@
1
- # @ulthon/ul-opencode-event
2
-
3
- **Never miss an OpenCode notification, even when you're away from your desk.**
4
-
5
- OpenCode multi-channel notification plugin that keeps you informed wherever you are. Perfect for remote development - get notified when tasks complete or errors occur via email or DingTalk, even if you're not at your terminal.
6
-
7
- ## Why ul-opencode-event?
8
-
9
- - 🌍 **Remote Development Friendly** - Desktop notifications don't work when you're away. Our plugin sends notifications to your email (and more channels coming soon), so you'll always know when your AI coding agent finishes a task.
10
- - 📬 **Multi-Channel Support** - Supports SMTP email and DingTalk robot, with more notification channels planned (webhook, Telegram, Slack, etc.)
11
- - **Real-time Alerts** - Get notified immediately when sessions complete (`idle`) or encounter errors (`error`)
12
- - 🔧 **Easy Configuration** - Simple JSON config with channel-level customization
13
-
14
- ## Roadmap
15
-
16
- | Channel | Status | Description |
17
- |---------|--------|-------------|
18
- | SMTP (Email) | Available | Email notifications via any SMTP server |
19
- | 🔜 Webhook | Planned | HTTP webhook for custom integrations |
20
- | 🔜 Telegram | Planned | Telegram bot notifications |
21
- | 🔜 Slack | Planned | Slack incoming webhooks |
22
- | DingTalk | Available | 钉钉机器人 |
23
- | 🔜 WeChat Work | Planned | 企业微信 |
24
-
25
- **Have a channel request?** Open an issue on our [Gitee repo](https://gitee.com/augushong/ul-opencode-event)!
26
-
27
- ## Quick Start
28
-
29
- ```bash
30
- # 1. 安装
31
- npm install @ulthon/ul-opencode-event
32
-
33
- # 2. 创建配置文件(项目根目录)
34
- cp node_modules/@ulthon/ul-opencode-event/examples/ul-opencode-event.json ./
35
-
36
- # 3. 编辑配置,填入你的 SMTP 信息
37
-
38
- # 4. 测试连接
39
- npx @ulthon/ul-opencode-event test
40
-
41
- # 5. 添加到 OpenCode 配置
42
- # 在 .opencode/settings.json 中添加:
43
- # { "plugin": ["@ulthon/ul-opencode-event"] }
44
- ```
45
- ## Installation
46
- ```bash
47
- npm install @ulthon/ul-opencode-event
48
- ```
49
- Then add to your OpenCode configuration (`.opencode/settings.json`):
50
- ```json
51
- {
52
- "plugin": ["@ulthon/ul-opencode-event"]
53
- }
54
- ```
55
- ## Configuration
56
- ### Configuration File Locations
57
- The plugin looks for configuration in these locations (in order of priority):
58
- 1. **Project-level**: `.opencode/ul-opencode-event.json` or `./ul-opencode-event.json`
59
- 2. **Global**: `~/.config/opencode/ul-opencode-event.json`
60
- Project-level configuration overrides global configuration (channels are merged by name).
61
- ### Minimal Configuration
62
- Create `ul-opencode-event.json` in your project root:
63
- ```json
64
- {
65
- "channels": [
66
- {
67
- "type": "smtp",
68
- "enabled": true,
69
- "name": "My Email",
70
- "config": {
71
- "host": "smtp.example.com",
72
- "port": 465,
73
- "secure": true,
74
- "auth": {
75
- "user": "your-email@example.com",
76
- "pass": "your-app-password"
77
- }
78
- },
79
- "recipients": ["receiver@example.com"],
80
- "events": {
81
- "idle": true,
82
- "error": true
83
- }
84
- }
85
- ]
86
- }
87
- ```
88
- ### Common SMTP Servers
89
- | Provider | Host | Port | Secure | Auth |
90
- |----------|------|------|--------|------|
91
- | QQ Mail | smtp.qq.com | 465 | true | Authorization code |
92
- | 163 Mail | smtp.163.com | 465 | true | Authorization code |
93
- | Gmail | smtp.gmail.com | 587 | false | App Password |
94
- | Outlook | smtp.office365.com | 587 | false | Password |
95
- > **Note**: For QQ/163 Mail, use the authorization code (授权码), not your login password.
96
- > For Gmail, use an App Password with 2FA enabled.
97
- ### Full Configuration Example
98
- ```json
99
- {
100
- "projectName": "my-project",
101
- "channels": [
102
- {
103
- "type": "smtp",
104
- "enabled": true,
105
- "name": "QQ邮箱",
106
- "config": {
107
- "host": "smtp.qq.com",
108
- "port": 465,
109
- "secure": true,
110
- "auth": {
111
- "user": "your@qq.com",
112
- "pass": "your-authorization-code"
113
- }
114
- },
115
- "recipients": ["admin@example.com", "dev-team@example.com"],
116
- "events": {
117
- "created": false,
118
- "idle": true,
119
- "error": true
120
- },
121
- "templates": {
122
- "idle": {
123
- "subject": "[{{projectName}}] 任务完成",
124
- "body": "任务已完成\n\n时间: {{timestamp}}\n耗时: {{duration}}\n消息: {{message}}"
125
- },
126
- "error": {
127
- "subject": "[{{projectName}}] 任务错误",
128
- "body": "任务发生错误\n\n时间: {{timestamp}}\n错误: {{error}}"
129
- }
130
- }
131
- }
132
- ]
133
- }
134
- ```
135
- ### Channel Configuration
136
- | Field | Type | Required | Description |
137
- |-------|------|----------|-------------|
138
- | `type` | string | Yes | Channel type: `smtp` |
139
- | `enabled` | boolean | No | Whether this channel is active (default: true) |
140
- | `name` | string | No | Channel name for identification |
141
- | `config` | object | Yes | SMTP configuration |
142
- | `recipients` | string[] | Yes | Email recipients |
143
- | `events` | object | No | Which events to notify |
144
- | `templates` | object | No | Custom templates for each event |
145
- ### SMTP Configuration
146
- | Field | Type | Required | Description |
147
- |-------|------|----------|-------------|
148
- | `host` | string | Yes | SMTP server hostname or IPv4/IPv6 address |
149
- | `port` | number | Yes | SMTP port (465 for SSL, 587 for STARTTLS) |
150
- | `secure` | boolean | No | Use SSL (default: true for port 465) |
151
- | `localAddress` | string | No | Local interface to bind for IPv4/IPv6 control |
152
- | `auth.user` | string | Yes | SMTP username (usually your email) |
153
- | `auth.pass` | string | Yes | SMTP password or authorization code |
154
-
155
- ### DingTalk 机器人配置
156
-
157
- 通过钉钉群自定义机器人接收 OpenCode 通知消息,支持 Markdown 格式和 @人功能。
158
-
159
- #### 获取 Webhook 和 Secret
160
-
161
- 1. 打开钉钉群,点击右上角 **群设置** > **智能群助手** > **添加机器人**
162
- 2. 选择 **自定义** 机器人,输入机器人名称和头像
163
- 3. **安全设置** 选择 **加签** 模式(推荐),复制生成的 **Secret**
164
- 4. 完成后复制 **Webhook 地址**(格式: `https://oapi.dingtalk.com/robot/send?access_token=xxx`)
165
-
166
- > **建议**: 始终开启加签模式,避免 Webhook 被恶意调用。
167
-
168
- #### 最小配置
169
-
170
- ```json
171
- {
172
- "channels": [
173
- {
174
- "type": "dingtalk",
175
- "enabled": true,
176
- "name": "钉钉通知",
177
- "config": {
178
- "webhook": "https://oapi.dingtalk.com/robot/send?access_token=YOUR_ACCESS_TOKEN"
179
- },
180
- "events": {
181
- "idle": true,
182
- "error": true
183
- }
184
- }
185
- ]
186
- }
187
- ```
188
-
189
- #### 完整配置示例
190
-
191
- ```json
192
- {
193
- "channels": [
194
- {
195
- "type": "dingtalk",
196
- "enabled": true,
197
- "name": "钉钉通知",
198
- "config": {
199
- "webhook": "https://oapi.dingtalk.com/robot/send?access_token=YOUR_ACCESS_TOKEN",
200
- "secret": "SEC-your-secret-key-here",
201
- "at": {
202
- "atMobiles": ["13800138000"],
203
- "atAll": false
204
- }
205
- },
206
- "events": {
207
- "created": false,
208
- "idle": true,
209
- "error": true
210
- },
211
- "templates": {
212
- "idle": {
213
- "body": "### [{{projectName}}] 任务完成\n\n**耗时**: {{duration}}\n**消息**: {{message}}\n\n> {{timestamp}}"
214
- },
215
- "error": {
216
- "body": "### [{{projectName}}] 任务错误\n\n**错误**: {{error}}\n\n> {{timestamp}}"
217
- }
218
- }
219
- }
220
- ]
221
- }
222
- ```
223
-
224
- #### DingTalk 配置字段说明
225
-
226
- | Field | Type | Required | Default | Description |
227
- |-------|------|----------|---------|-------------|
228
- | `webhook` | string | Yes | - | 钉钉机器人 Webhook 地址 |
229
- | `secret` | string | No | - | 加签密钥(安全设置选"加签"时提供) |
230
- | `at.atMobiles` | string[] | No | - | 要 @的成员手机号列表 |
231
- | `at.atAll` | boolean | No | false | 是否 @所有人 |
232
-
233
- > **注意**: 钉钉通道不需要 `recipients` 字段,消息发送到机器人所在的钉钉群。
234
-
235
- #### @人功能
236
-
237
- 钉钉支持在消息中 @指定群成员:
238
-
239
- - **@指定成员**: 在 `at.atMobiles` 中填写手机号数组,如 `["13800138000", "13900139000"]`
240
- - **@所有人**: 设置 `at.atAll` 为 `true`
241
- - **不@任何人**: 不配置 `at` 字段,或设为空对象 `{}`
242
-
243
- 默认行为是不 @任何人。如果同时设置了 `atMobiles` 和 `atAll=true`,将以 `atAll` 为准。
244
-
245
- #### 钉钉常见问题
246
-
247
- **Q: 签名失败 (signature mismatch) 怎么办?**
248
-
249
- A: 检查以下几点:
250
- 1. 确认 `secret` 值与钉钉后台显示的完全一致(注意前后空格)
251
- 2. 确认服务器时间与标准时间偏差不超过 1 小时
252
- 3. 如果使用代理/TUN 环境,确认网络能正常访问 `oapi.dingtalk.com`
253
-
254
- **Q: 消息发送频率有限制吗?**
255
-
256
- A: 有。每个机器人每条消息不超过 20 条/分钟。如果短时间内产生大量事件通知(如批量文件编辑),部分消息可能被限流丢弃。
257
-
258
- **Q: 钉钉 Markdown 支持哪些格式?**
259
-
260
- A: 钉钉 Markdown 支持有限的语法子集:
261
- - 标题: `#` `##` `###`
262
- - 加粗: `**text**`
263
- - 引用: `>`
264
- - 有序/无序列表
265
- - 链接: `[text](url)`
266
- - 图片: `![](url)`
267
-
268
- 不支持表格、代码块高亮、HTML 标签等。编写自定义模板时请使用支持的语法。
269
-
270
- **Q: 消息丢失或收不到怎么办?**
271
-
272
- A: 排查步骤:
273
- 1. 运行 `npx @ulthon/ul-opencode-event test --channel "钉钉通知"` 测试连接
274
- 2. 设置环境变量 `UL_OPENCODE_EVENT_DEBUG=1` 查看详细日志
275
- 3. 检查钉钉群中机器人是否已被移除或禁用
276
- 4. 确认 Webhook 地址中的 `access_token` 未过期(通常不会过期)
277
-
278
- ### IPv4/IPv6 Configuration
279
-
280
- By default, the plugin automatically supports both IPv4 and IPv6. Nodemailer will:
281
- - Resolve DNS to both IPv4 (A) and IPv6 (AAAA) records
282
- - Try both address families in parallel
283
- - Use whichever connects successfully
284
-
285
- #### Force IPv4
286
- If you want to force IPv4 connection (e.g., IPv6 is unavailable or unstable):
287
- ```json
288
- {
289
- "config": {
290
- "host": "smtp.qq.com",
291
- "port": 465,
292
- "secure": true,
293
- "localAddress": "0.0.0.0",
294
- "auth": {
295
- "user": "your@qq.com",
296
- "pass": "authorization-code"
297
- }
298
- }
299
- }
300
- ```
301
-
302
- #### Force IPv6
303
- If you want to force IPv6 connection:
304
- ```json
305
- {
306
- "config": {
307
- "host": "smtp.example.com",
308
- "port": 465,
309
- "secure": true,
310
- "localAddress": "::",
311
- "auth": {
312
- "user": "your@example.com",
313
- "pass": "your-password"
314
- }
315
- }
316
- }
317
- ```
318
-
319
- #### Use IPv6 Address as Host
320
- You can also use an IPv6 address directly as the host:
321
- ```json
322
- {
323
- "config": {
324
- "host": "2001:db8::1",
325
- "port": 465,
326
- "secure": true,
327
- "auth": {
328
- "user": "your@example.com",
329
- "pass": "your-password"
330
- }
331
- }
332
- }
333
- ```
334
-
335
- ### Events Configuration
336
- | Event | Default | Description |
337
- |-------|---------|-------------|
338
- | `created` | false | When a new session starts |
339
- | `idle` | true | When a session completes (AI finishes responding) |
340
- | `error` | true | When a session encounters an error |
341
- ### Template Variables
342
- | Variable | Description | Available Events |
343
- |----------|-------------|------------------|
344
- | `{{eventType}}` | Event type (created, idle, error) | All |
345
- | `{{timestamp}}` | ISO 8601 timestamp | All |
346
- | `{{projectName}}` | Project short name (directory basename or config value) | All |
347
- | `{{projectPath}}` | Project full path (e.g. `/home/user/my-project`) | All |
348
- | `{{sessionId}}` | Session ID | All |
349
- | `{{message}}` | Completion message | idle |
350
- | `{{duration}}` | Task duration | idle |
351
- | `{{error}}` | Error message | error |
352
- ## CLI Tool
353
- This package includes a CLI tool to test your notification channel configuration.
354
- ### Test Command
355
- ```bash
356
- # Test all channels (validates config, tests connection, sends test email)
357
- npx @ulthon/ul-opencode-event test
358
-
359
- # Test a specific channel by name
360
- npx @ulthon/ul-opencode-event test --channel "My Email"
361
-
362
- # Only verify connection without sending email
363
- npx @ulthon/ul-opencode-event test --no-send
364
- ```
365
- ### Test Output Example
366
- ```
367
- [ul-opencode-event-cli] Starting channel test...
368
- [ul-opencode-event-cli] Loaded project config: ./ul-opencode-event.json
369
- [ul-opencode-event-cli] Found 1 channel(s) to test
370
-
371
- [ul-opencode-event-cli] Testing channel: "My Email"
372
- Type: smtp
373
- Recipients: user@example.com
374
-
375
- [1/3] Validating configuration...
376
- [OK] Configuration is valid
377
-
378
- [2/3] Testing SMTP connection to smtp.example.com:465...
379
- [OK] Connection successful
380
-
381
- [3/3] Sending test email...
382
- [OK] Test email sent successfully
383
- Message ID: <xxx>
384
-
385
- [SUCCESS] Channel "My Email" passed all tests
386
-
387
- [ul-opencode-event-cli] Test Summary:
388
- Passed: 1
389
- Failed: 0
390
- ```
391
- ## Troubleshooting
392
- ### SMTP connection failed
393
- **Q: Does this plugin support IPv4? IPv6?**
394
- **A: Yes, both are supported.** The plugin uses nodemailer which automatically:
395
- - Resolves DNS to both IPv4 (A) and IPv6 (AAAA) records
396
- - Tries both address families in parallel
397
- - Falls back if one fails
398
- **Common issues:**
399
- 1. **Wrong password**: Use authorization code for QQ/163, App Password for Gmail
400
- 2. **Wrong port**: Use 465 for SSL, 587 for STARTTLS
401
- 3. **Firewall**: Ensure outbound connections to SMTP port are allowed
402
- 4. **IPv6 issues**: If your network has incomplete IPv6, connections may timeout
403
- - Check your network's IPv6 configuration
404
- - Contact your network administrator if needed
405
- ### Debug mode
406
- ```bash
407
- # Enable debug logging
408
- UL_OPENCODE_EVENT_DEBUG=1 npx @ulthon/ul-opencode-event test
409
- ```
410
- ### No notifications sent
411
- 1. Check if `enabled: true` is set
412
- 2. Check if the event type is enabled in `events`
413
- 3. Check SMTP credentials are correct
414
- 4. Ensure config file exists at correct location
415
- ## Local Development
416
- ```bash
417
- # Build
418
- npm run build
419
-
420
- # Test locally without publishing
421
- node dist/cli.js test
422
-
423
- # Or use npm link
424
- npm link
425
- npx @ulthon/ul-opencode-event test
426
-
427
- # Unlink when done
428
- npm unlink -g @ulthon/ul-opencode-event
429
- ```
430
- ## License
431
- MIT
1
+ # @ulthon/ul-opencode-event
2
+
3
+ **Never miss an OpenCode notification, even when you're away from your desk.**
4
+
5
+ OpenCode multi-channel notification plugin that keeps you informed wherever you are. Get notified when tasks complete or errors occur via email, DingTalk, or Feishu.
6
+
7
+ ## Why ul-opencode-event?
8
+
9
+ - **Remote Development Friendly** - Get notified when your AI coding agent finishes a task
10
+ - **Multi-Channel Support** - SMTP email, DingTalk robot, Feishu robot, with more planned
11
+ - **Real-time Alerts** - Immediate notifications on session `idle` or `error` events
12
+ - **Easy Configuration** - Simple JSON config with template variables
13
+
14
+ ## Roadmap
15
+
16
+ | Channel | Status | Description |
17
+ |---------|--------|-------------|
18
+ | SMTP (Email) | Available | Email notifications via any SMTP server |
19
+ | DingTalk | Available | DingTalk robot notifications |
20
+ | Feishu | Available | Feishu robot notifications |
21
+ | Webhook | Planned | HTTP webhook for custom integrations |
22
+ | Telegram | Planned | Telegram bot notifications |
23
+ | Slack | Planned | Slack incoming webhooks |
24
+ | WeChat Work | Planned | Enterprise WeChat |
25
+
26
+ **Have a channel request?** Open an issue on our [Gitee repo](https://gitee.com/augushong/ul-opencode-event)!
27
+
28
+ ## Quick Start
29
+
30
+ ```bash
31
+ npm install @ulthon/ul-opencode-event
32
+ cp node_modules/@ulthon/ul-opencode-event/examples/ul-opencode-event.json ./
33
+ # Edit config with your SMTP credentials
34
+ npx @ulthon/ul-opencode-event test
35
+ ```
36
+
37
+ Add to `~/.config/opencode/opencode.json`:
38
+
39
+ ```json
40
+ { "plugin": ["@ulthon/ul-opencode-event"] }
41
+ ```
42
+
43
+ ## Configuration
44
+
45
+ ### File Locations
46
+
47
+ 1. **Project-level**: `.opencode/ul-opencode-event.json` or `./ul-opencode-event.json`
48
+ 2. **Global**: `~/.config/opencode/ul-opencode-event.json`
49
+
50
+ Project-level config overrides global (channels merged by name).
51
+
52
+ ### Minimal SMTP Example
53
+
54
+ ```json
55
+ {
56
+ "channels": [{
57
+ "type": "smtp",
58
+ "enabled": true,
59
+ "name": "My Email",
60
+ "config": {
61
+ "host": "smtp.qq.com",
62
+ "port": 465,
63
+ "secure": true,
64
+ "auth": { "user": "your-email@example.com", "pass": "your-app-password" }
65
+ },
66
+ "recipients": ["receiver@example.com"],
67
+ "events": { "idle": true, "error": true }
68
+ }]
69
+ }
70
+ ```
71
+
72
+ ### Common SMTP Servers
73
+
74
+ | Provider | Host | Port | Auth |
75
+ |----------|------|------|------|
76
+ | QQ Mail | smtp.qq.com | 465 | Authorization code |
77
+ | 163 Mail | smtp.163.com | 465 | Authorization code |
78
+ | Gmail | smtp.gmail.com | 587 | App Password |
79
+ | Outlook | smtp.office365.com | 587 | Password |
80
+
81
+ > For QQ/163 use the authorization code, not login password. For Gmail use an App Password with 2FA.
82
+
83
+ ### Other Channels
84
+
85
+ - **DingTalk**: See [docs/dingtalk-channel.md](docs/dingtalk-channel.md) for robot setup, @mentions, and FAQ.
86
+ - **Feishu**: See [docs/feishu-channel.md](docs/feishu-channel.md) for robot setup, card format, and FAQ.
87
+
88
+ ### Events
89
+
90
+ | Event | Default | Description |
91
+ |-------|---------|-------------|
92
+ | `created` | false | When a new session starts |
93
+ | `idle` | true | When a session completes |
94
+ | `error` | true | When a session encounters an error |
95
+
96
+ ### Template Variables
97
+
98
+ | Variable | Description | Events |
99
+ |----------|-------------|--------|
100
+ | `{{eventType}}` | Event type (created, idle, error) | All |
101
+ | `{{timestamp}}` | ISO 8601 timestamp | All |
102
+ | `{{projectName}}` | Project short name | All |
103
+ | `{{projectPath}}` | Project full path | All |
104
+ | `{{sessionId}}` | Session ID | All |
105
+ | `{{message}}` | Completion message | idle |
106
+ | `{{duration}}` | Task duration | idle |
107
+ | `{{error}}` | Error message | error |
108
+
109
+ See [docs/template-configuration.md](docs/template-configuration.md) for customization details.
110
+
111
+ ## CLI Tool
112
+
113
+ ```bash
114
+ npx @ulthon/ul-opencode-event test # Test all channels
115
+ npx @ulthon/ul-opencode-event test --channel "My Email" # Test specific channel
116
+ npx @ulthon/ul-opencode-event test --no-send # Verify connection only
117
+ ```
118
+
119
+ ## Documentation
120
+
121
+ | Document | Description |
122
+ |----------|-------------|
123
+ | [Configuration](docs/configuration.md) | Config file locations, merge rules, events |
124
+ | [SMTP Channel](docs/smtp-channel.md) | Email configuration, IPv4/IPv6, FAQ |
125
+ | [DingTalk Channel](docs/dingtalk-channel.md) | Configuration, @mentions, FAQ |
126
+ | [Feishu Channel](docs/feishu-channel.md) | Configuration, card format, FAQ |
127
+ | [Template Configuration](docs/template-configuration.md) | Template variables and customization |
128
+ | [Channel Development](docs/channel-development.md) | Developing new notification channels |
129
+
130
+ ## Troubleshooting
131
+
132
+ - **Wrong password**: Use authorization code for QQ/163, App Password for Gmail
133
+ - **Wrong port**: Use 465 for SSL, 587 for STARTTLS
134
+ - **No notifications**: Check `enabled: true`, event types, and config file location
135
+ - **IPv4/IPv6**: Both supported; if IPv6 unstable, set `localAddress` to `"0.0.0.0"`
136
+ - **Debug mode**: `UL_OPENCODE_EVENT_DEBUG=1 npx @ulthon/ul-opencode-event test`
137
+
138
+ ## Local Development
139
+
140
+ ```bash
141
+ npm run build && node dist/cli.js test
142
+ # Or: npm link && npx @ulthon/ul-opencode-event test
143
+ ```
144
+
145
+ ## License
146
+
147
+ MIT