@mohak34/opencode-notifier 0.1.20-beta.0 → 0.1.21-beta.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.
- package/README.md +24 -39
- package/dist/index.js +71 -92
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,6 +52,7 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
|
|
|
52
52
|
"notification": true,
|
|
53
53
|
"timeout": 5,
|
|
54
54
|
"showProjectName": true,
|
|
55
|
+
"showSessionTitle": true,
|
|
55
56
|
"showIcon": true,
|
|
56
57
|
"notificationSystem": "osascript",
|
|
57
58
|
"command": {
|
|
@@ -68,11 +69,11 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
|
|
|
68
69
|
"question": { "sound": true, "notification": true }
|
|
69
70
|
},
|
|
70
71
|
"messages": {
|
|
71
|
-
"permission": "Session needs permission",
|
|
72
|
-
"complete": "Session has finished",
|
|
73
|
-
"subagent_complete": "Subagent task completed",
|
|
74
|
-
"error": "Session encountered an error",
|
|
75
|
-
"question": "Session has a question"
|
|
72
|
+
"permission": "Session needs permission: {sessionTitle}",
|
|
73
|
+
"complete": "Session has finished: {sessionTitle}",
|
|
74
|
+
"subagent_complete": "Subagent task completed: {sessionTitle}",
|
|
75
|
+
"error": "Session encountered an error: {sessionTitle}",
|
|
76
|
+
"question": "Session has a question: {sessionTitle}"
|
|
76
77
|
},
|
|
77
78
|
"sounds": {
|
|
78
79
|
"permission": null,
|
|
@@ -80,13 +81,6 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
|
|
|
80
81
|
"subagent_complete": null,
|
|
81
82
|
"error": null,
|
|
82
83
|
"question": null
|
|
83
|
-
},
|
|
84
|
-
"volumes": {
|
|
85
|
-
"permission": 1,
|
|
86
|
-
"complete": 1,
|
|
87
|
-
"subagent_complete": 1,
|
|
88
|
-
"error": 1,
|
|
89
|
-
"question": 1
|
|
90
84
|
}
|
|
91
85
|
}
|
|
92
86
|
```
|
|
@@ -101,6 +95,7 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
|
|
|
101
95
|
"notification": true,
|
|
102
96
|
"timeout": 5,
|
|
103
97
|
"showProjectName": true,
|
|
98
|
+
"showSessionTitle": true,
|
|
104
99
|
"showIcon": true,
|
|
105
100
|
"notificationSystem": "osascript"
|
|
106
101
|
}
|
|
@@ -110,6 +105,7 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
|
|
|
110
105
|
- `notification` - Turn notifications on/off (default: true)
|
|
111
106
|
- `timeout` - How long notifications show in seconds, Linux only (default: 5)
|
|
112
107
|
- `showProjectName` - Show folder name in notification title (default: true)
|
|
108
|
+
- `showSessionTitle` - Include the session title in notification messages via `{sessionTitle}` placeholder (default: true)
|
|
113
109
|
- `showIcon` - Show OpenCode icon, Windows/Linux only (default: true)
|
|
114
110
|
- `notificationSystem` - macOS only: `"osascript"` or `"node-notifier"` (default: "osascript")
|
|
115
111
|
|
|
@@ -146,15 +142,24 @@ Customize the notification text:
|
|
|
146
142
|
```json
|
|
147
143
|
{
|
|
148
144
|
"messages": {
|
|
149
|
-
"permission": "Session needs permission",
|
|
150
|
-
"complete": "Session has finished",
|
|
151
|
-
"subagent_complete": "Subagent task completed",
|
|
152
|
-
"error": "Session encountered an error",
|
|
153
|
-
"question": "Session has a question"
|
|
145
|
+
"permission": "Session needs permission: {sessionTitle}",
|
|
146
|
+
"complete": "Session has finished: {sessionTitle}",
|
|
147
|
+
"subagent_complete": "Subagent task completed: {sessionTitle}",
|
|
148
|
+
"error": "Session encountered an error: {sessionTitle}",
|
|
149
|
+
"question": "Session has a question: {sessionTitle}"
|
|
154
150
|
}
|
|
155
151
|
}
|
|
156
152
|
```
|
|
157
153
|
|
|
154
|
+
Messages support placeholder tokens that get replaced with actual values:
|
|
155
|
+
|
|
156
|
+
- `{sessionTitle}` - The title/summary of the current session (e.g. "Fix login bug")
|
|
157
|
+
- `{projectName}` - The project folder name
|
|
158
|
+
|
|
159
|
+
When `showSessionTitle` is `false`, `{sessionTitle}` is replaced with an empty string. Any trailing separators (`: `, ` - `, ` | `) are automatically cleaned up when a placeholder resolves to empty.
|
|
160
|
+
|
|
161
|
+
To disable session titles in messages without changing `showSessionTitle`, just remove the `{sessionTitle}` placeholder from your custom messages.
|
|
162
|
+
|
|
158
163
|
### Sounds
|
|
159
164
|
|
|
160
165
|
Use your own sound files:
|
|
@@ -176,29 +181,9 @@ Platform notes:
|
|
|
176
181
|
- Windows: Only .wav files work
|
|
177
182
|
- If file doesn't exist, falls back to bundled sound
|
|
178
183
|
|
|
179
|
-
### Volumes
|
|
180
|
-
|
|
181
|
-
Set per-event volume from `0` to `1`:
|
|
182
|
-
|
|
183
|
-
```json
|
|
184
|
-
{
|
|
185
|
-
"volumes": {
|
|
186
|
-
"permission": 0.6,
|
|
187
|
-
"complete": 0.3,
|
|
188
|
-
"subagent_complete": 0.15,
|
|
189
|
-
"error": 1,
|
|
190
|
-
"question": 0.7
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
- `0` = mute, `1` = full volume
|
|
196
|
-
- Values outside `0..1` are clamped automatically
|
|
197
|
-
- On Windows, playback still works but custom volume may not be honored by the default player
|
|
198
|
-
|
|
199
184
|
### Custom commands
|
|
200
185
|
|
|
201
|
-
Run your own script when something happens. Use `{event}` and `{
|
|
186
|
+
Run your own script when something happens. Use `{event}`, `{message}`, and `{sessionTitle}` as placeholders:
|
|
202
187
|
|
|
203
188
|
```json
|
|
204
189
|
{
|
|
@@ -213,7 +198,7 @@ Run your own script when something happens. Use `{event}` and `{message}` as pla
|
|
|
213
198
|
|
|
214
199
|
- `enabled` - Turn command on/off
|
|
215
200
|
- `path` - Path to your script/executable
|
|
216
|
-
- `args` - Arguments to pass, can use `{event}` and `{
|
|
201
|
+
- `args` - Arguments to pass, can use `{event}`, `{message}`, and `{sessionTitle}` tokens
|
|
217
202
|
- `minDuration` - Skip if response was quick, avoids spam (seconds)
|
|
218
203
|
|
|
219
204
|
#### Example: Log events to a file
|
package/dist/index.js
CHANGED
|
@@ -3743,6 +3743,7 @@ var DEFAULT_CONFIG = {
|
|
|
3743
3743
|
notification: true,
|
|
3744
3744
|
timeout: 5,
|
|
3745
3745
|
showProjectName: true,
|
|
3746
|
+
showSessionTitle: true,
|
|
3746
3747
|
showIcon: true,
|
|
3747
3748
|
notificationSystem: "osascript",
|
|
3748
3749
|
command: {
|
|
@@ -3758,11 +3759,11 @@ var DEFAULT_CONFIG = {
|
|
|
3758
3759
|
question: { ...DEFAULT_EVENT_CONFIG }
|
|
3759
3760
|
},
|
|
3760
3761
|
messages: {
|
|
3761
|
-
permission: "Session needs permission",
|
|
3762
|
-
complete: "Session has finished",
|
|
3763
|
-
subagent_complete: "Subagent task completed",
|
|
3764
|
-
error: "Session encountered an error",
|
|
3765
|
-
question: "Session has a question"
|
|
3762
|
+
permission: "Session needs permission: {sessionTitle}",
|
|
3763
|
+
complete: "Session has finished: {sessionTitle}",
|
|
3764
|
+
subagent_complete: "Subagent task completed: {sessionTitle}",
|
|
3765
|
+
error: "Session encountered an error: {sessionTitle}",
|
|
3766
|
+
question: "Session has a question: {sessionTitle}"
|
|
3766
3767
|
},
|
|
3767
3768
|
sounds: {
|
|
3768
3769
|
permission: null,
|
|
@@ -3770,13 +3771,6 @@ var DEFAULT_CONFIG = {
|
|
|
3770
3771
|
subagent_complete: null,
|
|
3771
3772
|
error: null,
|
|
3772
3773
|
question: null
|
|
3773
|
-
},
|
|
3774
|
-
volumes: {
|
|
3775
|
-
permission: 1,
|
|
3776
|
-
complete: 1,
|
|
3777
|
-
subagent_complete: 1,
|
|
3778
|
-
error: 1,
|
|
3779
|
-
question: 1
|
|
3780
3774
|
}
|
|
3781
3775
|
};
|
|
3782
3776
|
function getConfigPath() {
|
|
@@ -3797,18 +3791,6 @@ function parseEventConfig(userEvent, defaultConfig) {
|
|
|
3797
3791
|
notification: userEvent.notification ?? defaultConfig.notification
|
|
3798
3792
|
};
|
|
3799
3793
|
}
|
|
3800
|
-
function parseVolume(value, defaultVolume) {
|
|
3801
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
3802
|
-
return defaultVolume;
|
|
3803
|
-
}
|
|
3804
|
-
if (value < 0) {
|
|
3805
|
-
return 0;
|
|
3806
|
-
}
|
|
3807
|
-
if (value > 1) {
|
|
3808
|
-
return 1;
|
|
3809
|
-
}
|
|
3810
|
-
return value;
|
|
3811
|
-
}
|
|
3812
3794
|
function loadConfig() {
|
|
3813
3795
|
const configPath = getConfigPath();
|
|
3814
3796
|
if (!existsSync(configPath)) {
|
|
@@ -3831,6 +3813,7 @@ function loadConfig() {
|
|
|
3831
3813
|
notification: globalNotification,
|
|
3832
3814
|
timeout: typeof userConfig.timeout === "number" && userConfig.timeout > 0 ? userConfig.timeout : DEFAULT_CONFIG.timeout,
|
|
3833
3815
|
showProjectName: userConfig.showProjectName ?? DEFAULT_CONFIG.showProjectName,
|
|
3816
|
+
showSessionTitle: userConfig.showSessionTitle ?? DEFAULT_CONFIG.showSessionTitle,
|
|
3834
3817
|
showIcon: userConfig.showIcon ?? DEFAULT_CONFIG.showIcon,
|
|
3835
3818
|
notificationSystem: userConfig.notificationSystem === "node-notifier" ? "node-notifier" : "osascript",
|
|
3836
3819
|
command: {
|
|
@@ -3859,13 +3842,6 @@ function loadConfig() {
|
|
|
3859
3842
|
subagent_complete: userConfig.sounds?.subagent_complete ?? DEFAULT_CONFIG.sounds.subagent_complete,
|
|
3860
3843
|
error: userConfig.sounds?.error ?? DEFAULT_CONFIG.sounds.error,
|
|
3861
3844
|
question: userConfig.sounds?.question ?? DEFAULT_CONFIG.sounds.question
|
|
3862
|
-
},
|
|
3863
|
-
volumes: {
|
|
3864
|
-
permission: parseVolume(userConfig.volumes?.permission, DEFAULT_CONFIG.volumes.permission),
|
|
3865
|
-
complete: parseVolume(userConfig.volumes?.complete, DEFAULT_CONFIG.volumes.complete),
|
|
3866
|
-
subagent_complete: parseVolume(userConfig.volumes?.subagent_complete, DEFAULT_CONFIG.volumes.subagent_complete),
|
|
3867
|
-
error: parseVolume(userConfig.volumes?.error, DEFAULT_CONFIG.volumes.error),
|
|
3868
|
-
question: parseVolume(userConfig.volumes?.question, DEFAULT_CONFIG.volumes.question)
|
|
3869
3845
|
}
|
|
3870
3846
|
};
|
|
3871
3847
|
} catch {
|
|
@@ -3884,9 +3860,6 @@ function getMessage(config, event) {
|
|
|
3884
3860
|
function getSoundPath(config, event) {
|
|
3885
3861
|
return config.sounds[event];
|
|
3886
3862
|
}
|
|
3887
|
-
function getSoundVolume(config, event) {
|
|
3888
|
-
return config.volumes[event];
|
|
3889
|
-
}
|
|
3890
3863
|
function getIconPath(config) {
|
|
3891
3864
|
if (!config.showIcon) {
|
|
3892
3865
|
return;
|
|
@@ -3901,6 +3874,16 @@ function getIconPath(config) {
|
|
|
3901
3874
|
} catch {}
|
|
3902
3875
|
return;
|
|
3903
3876
|
}
|
|
3877
|
+
function interpolateMessage(message, context) {
|
|
3878
|
+
let result = message;
|
|
3879
|
+
const sessionTitle = context.sessionTitle || "";
|
|
3880
|
+
result = result.replaceAll("{sessionTitle}", sessionTitle);
|
|
3881
|
+
const projectName = context.projectName || "";
|
|
3882
|
+
result = result.replaceAll("{projectName}", projectName);
|
|
3883
|
+
result = result.replace(/\s*[:\-|]\s*$/, "").trim();
|
|
3884
|
+
result = result.replace(/\s{2,}/g, " ");
|
|
3885
|
+
return result;
|
|
3886
|
+
}
|
|
3904
3887
|
|
|
3905
3888
|
// src/notify.ts
|
|
3906
3889
|
var import_node_notifier = __toESM(require_node_notifier(), 1);
|
|
@@ -3968,8 +3951,6 @@ import { existsSync as existsSync2 } from "fs";
|
|
|
3968
3951
|
import { spawn } from "child_process";
|
|
3969
3952
|
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
3970
3953
|
var DEBOUNCE_MS2 = 1000;
|
|
3971
|
-
var FULL_VOLUME_PERCENT = 100;
|
|
3972
|
-
var FULL_VOLUME_PULSE = 65536;
|
|
3973
3954
|
var lastSoundTime = {};
|
|
3974
3955
|
function getBundledSoundPath(event) {
|
|
3975
3956
|
const soundFilename = `${event}.wav`;
|
|
@@ -4012,32 +3993,12 @@ async function runCommand(command, args) {
|
|
|
4012
3993
|
});
|
|
4013
3994
|
});
|
|
4014
3995
|
}
|
|
4015
|
-
function
|
|
4016
|
-
if (!Number.isFinite(volume)) {
|
|
4017
|
-
return 1;
|
|
4018
|
-
}
|
|
4019
|
-
if (volume < 0) {
|
|
4020
|
-
return 0;
|
|
4021
|
-
}
|
|
4022
|
-
if (volume > 1) {
|
|
4023
|
-
return 1;
|
|
4024
|
-
}
|
|
4025
|
-
return volume;
|
|
4026
|
-
}
|
|
4027
|
-
function toPercentVolume(volume) {
|
|
4028
|
-
return Math.round(volume * FULL_VOLUME_PERCENT);
|
|
4029
|
-
}
|
|
4030
|
-
function toPulseVolume(volume) {
|
|
4031
|
-
return Math.round(volume * FULL_VOLUME_PULSE);
|
|
4032
|
-
}
|
|
4033
|
-
async function playOnLinux(soundPath, volume) {
|
|
4034
|
-
const percentVolume = toPercentVolume(volume);
|
|
4035
|
-
const pulseVolume = toPulseVolume(volume);
|
|
3996
|
+
async function playOnLinux(soundPath) {
|
|
4036
3997
|
const players = [
|
|
4037
|
-
{ command: "paplay", args: [
|
|
3998
|
+
{ command: "paplay", args: [soundPath] },
|
|
4038
3999
|
{ command: "aplay", args: [soundPath] },
|
|
4039
|
-
{ command: "mpv", args: ["--no-video", "--no-terminal",
|
|
4040
|
-
{ command: "ffplay", args: ["-nodisp", "-autoexit", "-loglevel", "quiet",
|
|
4000
|
+
{ command: "mpv", args: ["--no-video", "--no-terminal", soundPath] },
|
|
4001
|
+
{ command: "ffplay", args: ["-nodisp", "-autoexit", "-loglevel", "quiet", soundPath] }
|
|
4041
4002
|
];
|
|
4042
4003
|
for (const player of players) {
|
|
4043
4004
|
try {
|
|
@@ -4048,21 +4009,20 @@ async function playOnLinux(soundPath, volume) {
|
|
|
4048
4009
|
}
|
|
4049
4010
|
}
|
|
4050
4011
|
}
|
|
4051
|
-
async function playOnMac(soundPath
|
|
4052
|
-
await runCommand("afplay", [
|
|
4012
|
+
async function playOnMac(soundPath) {
|
|
4013
|
+
await runCommand("afplay", [soundPath]);
|
|
4053
4014
|
}
|
|
4054
4015
|
async function playOnWindows(soundPath) {
|
|
4055
4016
|
const script = `& { (New-Object Media.SoundPlayer $args[0]).PlaySync() }`;
|
|
4056
4017
|
await runCommand("powershell", ["-c", script, soundPath]);
|
|
4057
4018
|
}
|
|
4058
|
-
async function playSound(event, customPath
|
|
4019
|
+
async function playSound(event, customPath) {
|
|
4059
4020
|
const now = Date.now();
|
|
4060
4021
|
if (lastSoundTime[event] && now - lastSoundTime[event] < DEBOUNCE_MS2) {
|
|
4061
4022
|
return;
|
|
4062
4023
|
}
|
|
4063
4024
|
lastSoundTime[event] = now;
|
|
4064
4025
|
const soundPath = getSoundFilePath(event, customPath);
|
|
4065
|
-
const normalizedVolume = normalizeVolume(volume);
|
|
4066
4026
|
if (!soundPath) {
|
|
4067
4027
|
return;
|
|
4068
4028
|
}
|
|
@@ -4070,10 +4030,10 @@ async function playSound(event, customPath, volume) {
|
|
|
4070
4030
|
try {
|
|
4071
4031
|
switch (os2) {
|
|
4072
4032
|
case "darwin":
|
|
4073
|
-
await playOnMac(soundPath
|
|
4033
|
+
await playOnMac(soundPath);
|
|
4074
4034
|
break;
|
|
4075
4035
|
case "linux":
|
|
4076
|
-
await playOnLinux(soundPath
|
|
4036
|
+
await playOnLinux(soundPath);
|
|
4077
4037
|
break;
|
|
4078
4038
|
case "win32":
|
|
4079
4039
|
await playOnWindows(soundPath);
|
|
@@ -4086,15 +4046,18 @@ async function playSound(event, customPath, volume) {
|
|
|
4086
4046
|
|
|
4087
4047
|
// src/command.ts
|
|
4088
4048
|
import { spawn as spawn2 } from "child_process";
|
|
4089
|
-
function substituteTokens(value, event, message) {
|
|
4090
|
-
|
|
4049
|
+
function substituteTokens(value, event, message, sessionTitle, projectName) {
|
|
4050
|
+
let result = value.replaceAll("{event}", event).replaceAll("{message}", message);
|
|
4051
|
+
result = result.replaceAll("{sessionTitle}", sessionTitle || "");
|
|
4052
|
+
result = result.replaceAll("{projectName}", projectName || "");
|
|
4053
|
+
return result;
|
|
4091
4054
|
}
|
|
4092
|
-
function runCommand2(config, event, message) {
|
|
4055
|
+
function runCommand2(config, event, message, sessionTitle, projectName) {
|
|
4093
4056
|
if (!config.command.enabled || !config.command.path) {
|
|
4094
4057
|
return;
|
|
4095
4058
|
}
|
|
4096
|
-
const args = (config.command.args ?? []).map((arg) => substituteTokens(arg, event, message));
|
|
4097
|
-
const command = substituteTokens(config.command.path, event, message);
|
|
4059
|
+
const args = (config.command.args ?? []).map((arg) => substituteTokens(arg, event, message, sessionTitle, projectName));
|
|
4060
|
+
const command = substituteTokens(config.command.path, event, message, sessionTitle, projectName);
|
|
4098
4061
|
const proc = spawn2(command, args, {
|
|
4099
4062
|
stdio: "ignore",
|
|
4100
4063
|
detached: true
|
|
@@ -4110,9 +4073,13 @@ function getNotificationTitle(config, projectName) {
|
|
|
4110
4073
|
}
|
|
4111
4074
|
return "OpenCode";
|
|
4112
4075
|
}
|
|
4113
|
-
async function handleEvent(config, eventType, projectName, elapsedSeconds) {
|
|
4076
|
+
async function handleEvent(config, eventType, projectName, elapsedSeconds, sessionTitle) {
|
|
4114
4077
|
const promises = [];
|
|
4115
|
-
const
|
|
4078
|
+
const rawMessage = getMessage(config, eventType);
|
|
4079
|
+
const message = interpolateMessage(rawMessage, {
|
|
4080
|
+
sessionTitle: config.showSessionTitle ? sessionTitle : null,
|
|
4081
|
+
projectName
|
|
4082
|
+
});
|
|
4116
4083
|
if (isEventNotificationEnabled(config, eventType)) {
|
|
4117
4084
|
const title = getNotificationTitle(config, projectName);
|
|
4118
4085
|
const iconPath = getIconPath(config);
|
|
@@ -4120,13 +4087,12 @@ async function handleEvent(config, eventType, projectName, elapsedSeconds) {
|
|
|
4120
4087
|
}
|
|
4121
4088
|
if (isEventSoundEnabled(config, eventType)) {
|
|
4122
4089
|
const customSoundPath = getSoundPath(config, eventType);
|
|
4123
|
-
|
|
4124
|
-
promises.push(playSound(eventType, customSoundPath, soundVolume));
|
|
4090
|
+
promises.push(playSound(eventType, customSoundPath));
|
|
4125
4091
|
}
|
|
4126
4092
|
const minDuration = config.command?.minDuration;
|
|
4127
4093
|
const shouldSkipCommand = typeof minDuration === "number" && Number.isFinite(minDuration) && minDuration > 0 && typeof elapsedSeconds === "number" && Number.isFinite(elapsedSeconds) && elapsedSeconds < minDuration;
|
|
4128
4094
|
if (!shouldSkipCommand) {
|
|
4129
|
-
runCommand2(config, eventType, message);
|
|
4095
|
+
runCommand2(config, eventType, message, sessionTitle, projectName);
|
|
4130
4096
|
}
|
|
4131
4097
|
await Promise.allSettled(promises);
|
|
4132
4098
|
}
|
|
@@ -4156,26 +4122,33 @@ async function getElapsedSinceLastPrompt(client, sessionID) {
|
|
|
4156
4122
|
} catch {}
|
|
4157
4123
|
return null;
|
|
4158
4124
|
}
|
|
4159
|
-
async function
|
|
4125
|
+
async function getSessionInfo(client, sessionID) {
|
|
4160
4126
|
try {
|
|
4161
4127
|
const response = await client.session.get({ path: { id: sessionID } });
|
|
4162
|
-
|
|
4163
|
-
|
|
4128
|
+
return {
|
|
4129
|
+
isChild: !!response.data?.parentID,
|
|
4130
|
+
title: response.data?.title ?? null
|
|
4131
|
+
};
|
|
4164
4132
|
} catch {
|
|
4165
|
-
return false;
|
|
4133
|
+
return { isChild: false, title: null };
|
|
4166
4134
|
}
|
|
4167
4135
|
}
|
|
4168
|
-
async function handleEventWithElapsedTime(client, config, eventType, projectName, event) {
|
|
4169
|
-
const
|
|
4170
|
-
const shouldLookupElapsed = !!config.command?.enabled && typeof config.command?.path === "string" && config.command.path.length > 0 && typeof minDuration === "number" && Number.isFinite(minDuration) && minDuration > 0;
|
|
4136
|
+
async function handleEventWithElapsedTime(client, config, eventType, projectName, event, preloadedSessionTitle) {
|
|
4137
|
+
const sessionID = getSessionIDFromEvent(event);
|
|
4171
4138
|
let elapsedSeconds = null;
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4139
|
+
let sessionTitle = preloadedSessionTitle ?? null;
|
|
4140
|
+
if (sessionID) {
|
|
4141
|
+
const minDuration = config.command?.minDuration;
|
|
4142
|
+
const shouldLookupElapsed = !!config.command?.enabled && typeof config.command?.path === "string" && config.command.path.length > 0 && typeof minDuration === "number" && Number.isFinite(minDuration) && minDuration > 0;
|
|
4143
|
+
if (shouldLookupElapsed) {
|
|
4175
4144
|
elapsedSeconds = await getElapsedSinceLastPrompt(client, sessionID);
|
|
4176
4145
|
}
|
|
4146
|
+
if (!sessionTitle && config.showSessionTitle) {
|
|
4147
|
+
const info = await getSessionInfo(client, sessionID);
|
|
4148
|
+
sessionTitle = info.title;
|
|
4149
|
+
}
|
|
4177
4150
|
}
|
|
4178
|
-
await handleEvent(config, eventType, projectName, elapsedSeconds);
|
|
4151
|
+
await handleEvent(config, eventType, projectName, elapsedSeconds, sessionTitle);
|
|
4179
4152
|
}
|
|
4180
4153
|
var NotifierPlugin = async ({ client, directory }) => {
|
|
4181
4154
|
const config = loadConfig();
|
|
@@ -4191,18 +4164,24 @@ var NotifierPlugin = async ({ client, directory }) => {
|
|
|
4191
4164
|
if (event.type === "session.idle") {
|
|
4192
4165
|
const sessionID = getSessionIDFromEvent(event);
|
|
4193
4166
|
if (sessionID) {
|
|
4194
|
-
const
|
|
4195
|
-
if (!isChild) {
|
|
4196
|
-
await handleEventWithElapsedTime(client, config, "complete", projectName, event);
|
|
4167
|
+
const sessionInfo = await getSessionInfo(client, sessionID);
|
|
4168
|
+
if (!sessionInfo.isChild) {
|
|
4169
|
+
await handleEventWithElapsedTime(client, config, "complete", projectName, event, sessionInfo.title);
|
|
4197
4170
|
} else {
|
|
4198
|
-
await handleEventWithElapsedTime(client, config, "subagent_complete", projectName, event);
|
|
4171
|
+
await handleEventWithElapsedTime(client, config, "subagent_complete", projectName, event, sessionInfo.title);
|
|
4199
4172
|
}
|
|
4200
4173
|
} else {
|
|
4201
4174
|
await handleEventWithElapsedTime(client, config, "complete", projectName, event);
|
|
4202
4175
|
}
|
|
4203
4176
|
}
|
|
4204
4177
|
if (event.type === "session.error") {
|
|
4205
|
-
|
|
4178
|
+
const sessionID = getSessionIDFromEvent(event);
|
|
4179
|
+
let sessionTitle = null;
|
|
4180
|
+
if (sessionID && config.showSessionTitle) {
|
|
4181
|
+
const info = await getSessionInfo(client, sessionID);
|
|
4182
|
+
sessionTitle = info.title;
|
|
4183
|
+
}
|
|
4184
|
+
await handleEventWithElapsedTime(client, config, "error", projectName, event, sessionTitle);
|
|
4206
4185
|
}
|
|
4207
4186
|
},
|
|
4208
4187
|
"permission.ask": async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mohak34/opencode-notifier",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21-beta.0",
|
|
4
4
|
"description": "OpenCode plugin that sends system notifications and plays sounds when permission is needed, generation completes, or errors occur",
|
|
5
5
|
"author": "mohak34",
|
|
6
6
|
"license": "MIT",
|