@ulthon/ul-opencode-event 0.1.19 → 0.1.20
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 +9 -7
- package/dist/cli.js +1 -1
- package/dist/dingtalk.js +3 -2
- package/dist/handler.js +1 -0
- package/dist/index.js +5 -2
- package/dist/locales.js +36 -36
- package/dist/types.d.ts +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,6 +97,7 @@ Create `ul-opencode-event.json` in your project root:
|
|
|
97
97
|
### Full Configuration Example
|
|
98
98
|
```json
|
|
99
99
|
{
|
|
100
|
+
"projectName": "my-project",
|
|
100
101
|
"channels": [
|
|
101
102
|
{
|
|
102
103
|
"type": "smtp",
|
|
@@ -119,12 +120,12 @@ Create `ul-opencode-event.json` in your project root:
|
|
|
119
120
|
},
|
|
120
121
|
"templates": {
|
|
121
122
|
"idle": {
|
|
122
|
-
"subject": "[
|
|
123
|
-
"body": "任务已完成\n\n
|
|
123
|
+
"subject": "[{{projectName}}] 任务完成",
|
|
124
|
+
"body": "任务已完成\n\n时间: {{timestamp}}\n耗时: {{duration}}\n消息: {{message}}"
|
|
124
125
|
},
|
|
125
126
|
"error": {
|
|
126
|
-
"subject": "[
|
|
127
|
-
"body": "任务发生错误\n\n
|
|
127
|
+
"subject": "[{{projectName}}] 任务错误",
|
|
128
|
+
"body": "任务发生错误\n\n时间: {{timestamp}}\n错误: {{error}}"
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
}
|
|
@@ -209,10 +210,10 @@ Create `ul-opencode-event.json` in your project root:
|
|
|
209
210
|
},
|
|
210
211
|
"templates": {
|
|
211
212
|
"idle": {
|
|
212
|
-
"body": "
|
|
213
|
+
"body": "### [{{projectName}}] 任务完成\n\n**耗时**: {{duration}}\n**消息**: {{message}}\n\n> {{timestamp}}"
|
|
213
214
|
},
|
|
214
215
|
"error": {
|
|
215
|
-
"body": "
|
|
216
|
+
"body": "### [{{projectName}}] 任务错误\n\n**错误**: {{error}}\n\n> {{timestamp}}"
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
219
|
}
|
|
@@ -342,7 +343,8 @@ You can also use an IPv6 address directly as the host:
|
|
|
342
343
|
|----------|-------------|------------------|
|
|
343
344
|
| `{{eventType}}` | Event type (created, idle, error) | All |
|
|
344
345
|
| `{{timestamp}}` | ISO 8601 timestamp | All |
|
|
345
|
-
| `{{projectName}}` | Project name | All |
|
|
346
|
+
| `{{projectName}}` | Project short name (directory basename or config value) | All |
|
|
347
|
+
| `{{projectPath}}` | Project full path (e.g. `/home/user/my-project`) | All |
|
|
346
348
|
| `{{sessionId}}` | Session ID | All |
|
|
347
349
|
| `{{message}}` | Completion message | idle |
|
|
348
350
|
| `{{duration}}` | Task duration | idle |
|
package/dist/cli.js
CHANGED
|
@@ -415,7 +415,7 @@ Type: ${channel.type}
|
|
|
415
415
|
Time: ${timestamp}
|
|
416
416
|
|
|
417
417
|
If you received this message, your DingTalk notification channel is configured correctly.`;
|
|
418
|
-
const formattedMessage = formatter.format(`[Test] OpenCode Notification Channel Test - ${timestamp}`, testBody, { eventType: 'idle', timestamp, projectName: 'test', sessionId: 'cli-test', sessionType: 'main' });
|
|
418
|
+
const formattedMessage = formatter.format(`[Test] OpenCode Notification Channel Test - ${timestamp}`, testBody, { eventType: 'idle', timestamp, projectName: 'test', sessionId: 'cli-test', sessionType: 'main', projectPath: process.cwd() });
|
|
419
419
|
// Build URL with signing if secret is configured
|
|
420
420
|
let targetUrl = config.webhook.trim();
|
|
421
421
|
if (config.secret) {
|
package/dist/dingtalk.js
CHANGED
|
@@ -72,9 +72,10 @@ export function createDingTalkFormatter(config) {
|
|
|
72
72
|
const title = rawTitle.length > MAX_TITLE_LENGTH
|
|
73
73
|
? rawTitle.slice(0, MAX_TITLE_LENGTH) + '...'
|
|
74
74
|
: rawTitle;
|
|
75
|
-
// 2.
|
|
75
|
+
// 2. Insert subject heading first
|
|
76
|
+
const processedLines = [`### ${escapeMarkdown(title)}`];
|
|
77
|
+
// 3. Process body lines
|
|
76
78
|
const lines = body.split('\n');
|
|
77
|
-
const processedLines = [];
|
|
78
79
|
for (const line of lines) {
|
|
79
80
|
const trimmed = line.trim();
|
|
80
81
|
// Check for key: value pattern -> **key**: value
|
package/dist/handler.js
CHANGED
|
@@ -98,6 +98,7 @@ export function createEventHandler(config) {
|
|
|
98
98
|
todoSummary: payload.todoSummary,
|
|
99
99
|
sessionType: payload.sessionType,
|
|
100
100
|
parentSessionId: payload.parentSessionId,
|
|
101
|
+
projectPath: payload.projectPath,
|
|
101
102
|
};
|
|
102
103
|
// Render templates
|
|
103
104
|
const subject = template.subject ? renderTemplate(template.subject, variables) : '';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
1
2
|
import { loadConfig } from './config.js';
|
|
2
3
|
import { createEventHandler } from './handler.js';
|
|
3
4
|
import { getDefaultTimezone } from './locales.js';
|
|
@@ -20,10 +21,11 @@ function resolveSessionType(sessionInfo) {
|
|
|
20
21
|
/**
|
|
21
22
|
* Convert SDK Event to internal EventPayload
|
|
22
23
|
*/
|
|
23
|
-
function eventToPayload(event, projectName, sessionInfo, timezone) {
|
|
24
|
+
function eventToPayload(event, projectName, sessionInfo, timezone, projectPath) {
|
|
24
25
|
const basePayload = {
|
|
25
26
|
timestamp: formatTimestamp(new Date().toISOString(), timezone),
|
|
26
27
|
projectName,
|
|
28
|
+
projectPath,
|
|
27
29
|
sessionId: '',
|
|
28
30
|
sessionTitle: undefined,
|
|
29
31
|
};
|
|
@@ -276,7 +278,8 @@ export const NotificationPlugin = async (ctx) => {
|
|
|
276
278
|
if (DEBUG_ENABLED && !sessionInfo && typeof event.properties.sessionID === 'string') {
|
|
277
279
|
logger.debug(`[RESOLVED] MISS sessionID=${event.properties.sessionID} - sessionMap has no entry`);
|
|
278
280
|
}
|
|
279
|
-
const
|
|
281
|
+
const resolvedProjectName = config.projectName || path.basename(ctx.directory) || 'unknown-project';
|
|
282
|
+
const payload = eventToPayload(event, resolvedProjectName, sessionInfo, resolvedTimezone, ctx.directory);
|
|
280
283
|
if (!payload) {
|
|
281
284
|
return;
|
|
282
285
|
}
|
package/dist/locales.js
CHANGED
|
@@ -16,78 +16,78 @@ const LOCALE_TIMEZONE = {
|
|
|
16
16
|
const localeTemplates = {
|
|
17
17
|
en: {
|
|
18
18
|
created: {
|
|
19
|
-
subject: '[
|
|
20
|
-
body: 'Task started at {{timestamp}}\
|
|
19
|
+
subject: '[{{projectName}}] Task Started',
|
|
20
|
+
body: 'Task started at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})',
|
|
21
21
|
},
|
|
22
22
|
idle: {
|
|
23
|
-
subject: '[
|
|
24
|
-
body: 'Task completed at {{timestamp}}\
|
|
23
|
+
subject: '[{{projectName}}] Task Completed',
|
|
24
|
+
body: 'Task completed at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})\nMessage: {{message}}\nDuration: {{duration}}',
|
|
25
25
|
},
|
|
26
26
|
error: {
|
|
27
|
-
subject: '[
|
|
28
|
-
body: 'Error at {{timestamp}}\
|
|
27
|
+
subject: '[{{projectName}}] Task Error',
|
|
28
|
+
body: 'Error at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})\nError: {{error}}',
|
|
29
29
|
},
|
|
30
30
|
question: {
|
|
31
|
-
subject: '[
|
|
32
|
-
body: 'OpenCode has a question at {{timestamp}}\
|
|
31
|
+
subject: '[{{projectName}}] Question',
|
|
32
|
+
body: 'OpenCode has a question at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})\nQuestion: {{title}}\n{{questionText}}',
|
|
33
33
|
},
|
|
34
34
|
permission: {
|
|
35
|
-
subject: '[
|
|
36
|
-
body: 'Permission needed at {{timestamp}}\
|
|
35
|
+
subject: '[{{projectName}}] Permission Required',
|
|
36
|
+
body: 'Permission needed at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})\n{{title}} ({{permissionType}})',
|
|
37
37
|
},
|
|
38
38
|
status: {
|
|
39
|
-
subject: '[
|
|
40
|
-
body: 'Session status changed at {{timestamp}}\
|
|
39
|
+
subject: '[{{projectName}}] Status Change',
|
|
40
|
+
body: 'Session status changed at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})\nStatus: {{statusType}}',
|
|
41
41
|
},
|
|
42
42
|
command: {
|
|
43
|
-
subject: '[
|
|
44
|
-
body: 'Command executed at {{timestamp}}\
|
|
43
|
+
subject: '[{{projectName}}] Command Executed',
|
|
44
|
+
body: 'Command executed at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})\nCommand: {{commandName}} {{arguments}}',
|
|
45
45
|
},
|
|
46
46
|
fileEdited: {
|
|
47
|
-
subject: '[
|
|
48
|
-
body: 'File edited at {{timestamp}}\
|
|
47
|
+
subject: '[{{projectName}}] File Edited',
|
|
48
|
+
body: 'File edited at {{timestamp}}\nFile: {{filePath}}',
|
|
49
49
|
},
|
|
50
50
|
todo: {
|
|
51
|
-
subject: '[
|
|
52
|
-
body: 'Todos updated at {{timestamp}}\
|
|
51
|
+
subject: '[{{projectName}}] Todos Updated',
|
|
52
|
+
body: 'Todos updated at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})\n{{todoSummary}}',
|
|
53
53
|
},
|
|
54
54
|
},
|
|
55
55
|
zh: {
|
|
56
56
|
created: {
|
|
57
|
-
subject: '[
|
|
58
|
-
body: '任务启动时间: {{timestamp}}\n
|
|
57
|
+
subject: '[{{projectName}}] 任务已启动',
|
|
58
|
+
body: '任务启动时间: {{timestamp}}\n会话: {{sessionTitle}} ({{sessionId}})',
|
|
59
59
|
},
|
|
60
60
|
idle: {
|
|
61
|
-
subject: '[
|
|
62
|
-
body: '任务完成时间: {{timestamp}}\n
|
|
61
|
+
subject: '[{{projectName}}] 任务已完成',
|
|
62
|
+
body: '任务完成时间: {{timestamp}}\n会话: {{sessionTitle}} ({{sessionId}})\n消息: {{message}}\n耗时: {{duration}}',
|
|
63
63
|
},
|
|
64
64
|
error: {
|
|
65
|
-
subject: '[
|
|
66
|
-
body: '错误发生时间: {{timestamp}}\n
|
|
65
|
+
subject: '[{{projectName}}] 任务出错',
|
|
66
|
+
body: '错误发生时间: {{timestamp}}\n会话: {{sessionTitle}} ({{sessionId}})\n错误信息: {{error}}',
|
|
67
67
|
},
|
|
68
68
|
question: {
|
|
69
|
-
subject: '[
|
|
70
|
-
body: 'OpenCode 在 {{timestamp}} 向您提问\n
|
|
69
|
+
subject: '[{{projectName}}] AI 提问',
|
|
70
|
+
body: 'OpenCode 在 {{timestamp}} 向您提问\n会话: {{sessionTitle}} ({{sessionId}})\n问题: {{title}}\n{{questionText}}',
|
|
71
71
|
},
|
|
72
72
|
permission: {
|
|
73
|
-
subject: '[
|
|
74
|
-
body: '{{timestamp}} 需要权限确认\n
|
|
73
|
+
subject: '[{{projectName}}] 需要权限确认',
|
|
74
|
+
body: '{{timestamp}} 需要权限确认\n会话: {{sessionTitle}} ({{sessionId}})\n{{title}} ({{permissionType}})',
|
|
75
75
|
},
|
|
76
76
|
status: {
|
|
77
|
-
subject: '[
|
|
78
|
-
body: '会话状态变更时间: {{timestamp}}\n
|
|
77
|
+
subject: '[{{projectName}}] 状态变更',
|
|
78
|
+
body: '会话状态变更时间: {{timestamp}}\n会话: {{sessionTitle}} ({{sessionId}})\n状态: {{statusType}}',
|
|
79
79
|
},
|
|
80
80
|
command: {
|
|
81
|
-
subject: '[
|
|
82
|
-
body: '命令执行时间: {{timestamp}}\n
|
|
81
|
+
subject: '[{{projectName}}] 命令已执行',
|
|
82
|
+
body: '命令执行时间: {{timestamp}}\n会话: {{sessionTitle}} ({{sessionId}})\n命令: {{commandName}} {{arguments}}',
|
|
83
83
|
},
|
|
84
84
|
fileEdited: {
|
|
85
|
-
subject: '[
|
|
86
|
-
body: '文件编辑时间: {{timestamp}}\n
|
|
85
|
+
subject: '[{{projectName}}] 文件已编辑',
|
|
86
|
+
body: '文件编辑时间: {{timestamp}}\n文件: {{filePath}}',
|
|
87
87
|
},
|
|
88
88
|
todo: {
|
|
89
|
-
subject: '[
|
|
90
|
-
body: 'Todo 更新时间: {{timestamp}}\n
|
|
89
|
+
subject: '[{{projectName}}] Todo 已更新',
|
|
90
|
+
body: 'Todo 更新时间: {{timestamp}}\n会话: {{sessionTitle}} ({{sessionId}})\n{{todoSummary}}',
|
|
91
91
|
},
|
|
92
92
|
},
|
|
93
93
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export interface NotificationConfig {
|
|
|
22
22
|
channels: Channel[];
|
|
23
23
|
timezone?: string;
|
|
24
24
|
language?: LocaleType;
|
|
25
|
+
/** 项目简称(默认取目录名 basename) */
|
|
26
|
+
projectName?: string;
|
|
25
27
|
includeSessionTypes?: SessionType[];
|
|
26
28
|
/** 自动更新开关(默认 true) */
|
|
27
29
|
auto_update?: boolean;
|
|
@@ -128,6 +130,8 @@ export interface EventPayload {
|
|
|
128
130
|
eventType: EventType;
|
|
129
131
|
timestamp: string;
|
|
130
132
|
projectName: string;
|
|
133
|
+
/** 项目完整路径 */
|
|
134
|
+
projectPath?: string;
|
|
131
135
|
sessionId: string;
|
|
132
136
|
message?: string;
|
|
133
137
|
error?: string;
|
package/package.json
CHANGED