@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 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": "[OpenCode] 任务完成 - {{projectName}}",
123
- "body": "任务已完成\n\n项目: {{projectName}}\n时间: {{timestamp}}\n耗时: {{duration}}\n消息: {{message}}"
123
+ "subject": "[{{projectName}}] 任务完成",
124
+ "body": "任务已完成\n\n时间: {{timestamp}}\n耗时: {{duration}}\n消息: {{message}}"
124
125
  },
125
126
  "error": {
126
- "subject": "[OpenCode] 任务错误 - {{projectName}}",
127
- "body": "任务发生错误\n\n项目: {{projectName}}\n时间: {{timestamp}}\n错误: {{error}}"
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": "**OpenCode 任务完成**\n\n项目: {{projectName}}\n时间: {{timestamp}}\n耗时: {{duration}}\n消息: {{message}}"
213
+ "body": "### [{{projectName}}] 任务完成\n\n**耗时**: {{duration}}\n**消息**: {{message}}\n\n> {{timestamp}}"
213
214
  },
214
215
  "error": {
215
- "body": "**OpenCode 任务错误**\n\n项目: {{projectName}}\n时间: {{timestamp}}\n错误: {{error}}"
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. Process body lines
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 payload = eventToPayload(event, ctx.directory, sessionInfo, resolvedTimezone);
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: '[OpenCode] Task Started - {{projectName}}',
20
- body: 'Task started at {{timestamp}}\nProject: {{projectName}}\nSession: {{sessionTitle}} ({{sessionId}})\nAgent Type: {{sessionType}}\nParent Session: {{parentSessionId}}',
19
+ subject: '[{{projectName}}] Task Started',
20
+ body: 'Task started at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})',
21
21
  },
22
22
  idle: {
23
- subject: '[OpenCode] Task Completed - {{projectName}}',
24
- body: 'Task completed at {{timestamp}}\nProject: {{projectName}}\nSession: {{sessionTitle}} ({{sessionId}})\nMessage: {{message}}\nDuration: {{duration}}\nAgent Type: {{sessionType}}\nParent Session: {{parentSessionId}}',
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: '[OpenCode] Task Error - {{projectName}}',
28
- body: 'Error at {{timestamp}}\nProject: {{projectName}}\nSession: {{sessionTitle}} ({{sessionId}})\nError: {{error}}\nAgent Type: {{sessionType}}\nParent Session: {{parentSessionId}}',
27
+ subject: '[{{projectName}}] Task Error',
28
+ body: 'Error at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})\nError: {{error}}',
29
29
  },
30
30
  question: {
31
- subject: '[OpenCode] Question - {{projectName}}',
32
- body: 'OpenCode has a question at {{timestamp}}\nProject: {{projectName}}\nSession: {{sessionTitle}} ({{sessionId}})\nQuestion: {{title}}\n{{questionText}}\nAgent Type: {{sessionType}}\nParent Session: {{parentSessionId}}',
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: '[OpenCode] Permission Required - {{projectName}}',
36
- body: 'Permission needed at {{timestamp}}\nProject: {{projectName}}\nSession: {{sessionTitle}} ({{sessionId}})\n{{title}} ({{permissionType}})\nAgent Type: {{sessionType}}\nParent Session: {{parentSessionId}}',
35
+ subject: '[{{projectName}}] Permission Required',
36
+ body: 'Permission needed at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})\n{{title}} ({{permissionType}})',
37
37
  },
38
38
  status: {
39
- subject: '[OpenCode] Status Change - {{projectName}}',
40
- body: 'Session status changed at {{timestamp}}\nProject: {{projectName}}\nSession: {{sessionTitle}} ({{sessionId}})\nStatus: {{statusType}}\nAgent Type: {{sessionType}}\nParent Session: {{parentSessionId}}',
39
+ subject: '[{{projectName}}] Status Change',
40
+ body: 'Session status changed at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})\nStatus: {{statusType}}',
41
41
  },
42
42
  command: {
43
- subject: '[OpenCode] Command Executed - {{projectName}}',
44
- body: 'Command executed at {{timestamp}}\nProject: {{projectName}}\nSession: {{sessionTitle}} ({{sessionId}})\nCommand: {{commandName}} {{arguments}}\nAgent Type: {{sessionType}}\nParent Session: {{parentSessionId}}',
43
+ subject: '[{{projectName}}] Command Executed',
44
+ body: 'Command executed at {{timestamp}}\nSession: {{sessionTitle}} ({{sessionId}})\nCommand: {{commandName}} {{arguments}}',
45
45
  },
46
46
  fileEdited: {
47
- subject: '[OpenCode] File Edited - {{projectName}}',
48
- body: 'File edited at {{timestamp}}\nProject: {{projectName}}\nFile: {{filePath}}\nAgent Type: {{sessionType}}\nParent Session: {{parentSessionId}}',
47
+ subject: '[{{projectName}}] File Edited',
48
+ body: 'File edited at {{timestamp}}\nFile: {{filePath}}',
49
49
  },
50
50
  todo: {
51
- subject: '[OpenCode] Todos Updated - {{projectName}}',
52
- body: 'Todos updated at {{timestamp}}\nProject: {{projectName}}\nSession: {{sessionTitle}} ({{sessionId}})\n{{todoSummary}}\nAgent Type: {{sessionType}}\nParent Session: {{parentSessionId}}',
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: '[OpenCode] 任务已启动 - {{projectName}}',
58
- body: '任务启动时间: {{timestamp}}\n项目: {{projectName}}\n会话: {{sessionTitle}} ({{sessionId}})\n代理类型: {{sessionType}}\n父会话: {{parentSessionId}}',
57
+ subject: '[{{projectName}}] 任务已启动',
58
+ body: '任务启动时间: {{timestamp}}\n会话: {{sessionTitle}} ({{sessionId}})',
59
59
  },
60
60
  idle: {
61
- subject: '[OpenCode] 任务已完成 - {{projectName}}',
62
- body: '任务完成时间: {{timestamp}}\n项目: {{projectName}}\n会话: {{sessionTitle}} ({{sessionId}})\n消息: {{message}}\n耗时: {{duration}}\n代理类型: {{sessionType}}\n父会话: {{parentSessionId}}',
61
+ subject: '[{{projectName}}] 任务已完成',
62
+ body: '任务完成时间: {{timestamp}}\n会话: {{sessionTitle}} ({{sessionId}})\n消息: {{message}}\n耗时: {{duration}}',
63
63
  },
64
64
  error: {
65
- subject: '[OpenCode] 任务出错 - {{projectName}}',
66
- body: '错误发生时间: {{timestamp}}\n项目: {{projectName}}\n会话: {{sessionTitle}} ({{sessionId}})\n错误信息: {{error}}\n代理类型: {{sessionType}}\n父会话: {{parentSessionId}}',
65
+ subject: '[{{projectName}}] 任务出错',
66
+ body: '错误发生时间: {{timestamp}}\n会话: {{sessionTitle}} ({{sessionId}})\n错误信息: {{error}}',
67
67
  },
68
68
  question: {
69
- subject: '[OpenCode] AI 提问 - {{projectName}}',
70
- body: 'OpenCode 在 {{timestamp}} 向您提问\n项目: {{projectName}}\n会话: {{sessionTitle}} ({{sessionId}})\n问题: {{title}}\n{{questionText}}\n代理类型: {{sessionType}}\n父会话: {{parentSessionId}}',
69
+ subject: '[{{projectName}}] AI 提问',
70
+ body: 'OpenCode 在 {{timestamp}} 向您提问\n会话: {{sessionTitle}} ({{sessionId}})\n问题: {{title}}\n{{questionText}}',
71
71
  },
72
72
  permission: {
73
- subject: '[OpenCode] 需要权限确认 - {{projectName}}',
74
- body: '{{timestamp}} 需要权限确认\n项目: {{projectName}}\n会话: {{sessionTitle}} ({{sessionId}})\n{{title}} ({{permissionType}})\n代理类型: {{sessionType}}\n父会话: {{parentSessionId}}',
73
+ subject: '[{{projectName}}] 需要权限确认',
74
+ body: '{{timestamp}} 需要权限确认\n会话: {{sessionTitle}} ({{sessionId}})\n{{title}} ({{permissionType}})',
75
75
  },
76
76
  status: {
77
- subject: '[OpenCode] 状态变更 - {{projectName}}',
78
- body: '会话状态变更时间: {{timestamp}}\n项目: {{projectName}}\n会话: {{sessionTitle}} ({{sessionId}})\n状态: {{statusType}}\n代理类型: {{sessionType}}\n父会话: {{parentSessionId}}',
77
+ subject: '[{{projectName}}] 状态变更',
78
+ body: '会话状态变更时间: {{timestamp}}\n会话: {{sessionTitle}} ({{sessionId}})\n状态: {{statusType}}',
79
79
  },
80
80
  command: {
81
- subject: '[OpenCode] 命令已执行 - {{projectName}}',
82
- body: '命令执行时间: {{timestamp}}\n项目: {{projectName}}\n会话: {{sessionTitle}} ({{sessionId}})\n命令: {{commandName}} {{arguments}}\n代理类型: {{sessionType}}\n父会话: {{parentSessionId}}',
81
+ subject: '[{{projectName}}] 命令已执行',
82
+ body: '命令执行时间: {{timestamp}}\n会话: {{sessionTitle}} ({{sessionId}})\n命令: {{commandName}} {{arguments}}',
83
83
  },
84
84
  fileEdited: {
85
- subject: '[OpenCode] 文件已编辑 - {{projectName}}',
86
- body: '文件编辑时间: {{timestamp}}\n项目: {{projectName}}\n文件: {{filePath}}\n代理类型: {{sessionType}}\n父会话: {{parentSessionId}}',
85
+ subject: '[{{projectName}}] 文件已编辑',
86
+ body: '文件编辑时间: {{timestamp}}\n文件: {{filePath}}',
87
87
  },
88
88
  todo: {
89
- subject: '[OpenCode] Todo 已更新 - {{projectName}}',
90
- body: 'Todo 更新时间: {{timestamp}}\n项目: {{projectName}}\n会话: {{sessionTitle}} ({{sessionId}})\n{{todoSummary}}\n代理类型: {{sessionType}}\n父会话: {{parentSessionId}}',
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulthon/ul-opencode-event",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "description": "OpenCode notification plugin - sends notifications via email or DingTalk when session events occur",
5
5
  "author": "augushong",
6
6
  "license": "MIT",