@mohak34/opencode-notifier 0.1.19-beta.0 → 0.1.20-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.
Files changed (3) hide show
  1. package/README.md +198 -195
  2. package/dist/index.js +63 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  OpenCode plugin that plays sounds and sends system notifications when permission is needed, generation completes, errors occur, or the question tool is invoked. Works on macOS, Linux, and Windows.
4
4
 
5
- ## Installation
5
+ ## Quick Start
6
6
 
7
- Add the plugin to your `opencode.json` or `opencode.jsonc`:
7
+ Add this to your `opencode.json`:
8
8
 
9
9
  ```json
10
10
  {
@@ -12,99 +12,39 @@ Add the plugin to your `opencode.json` or `opencode.jsonc`:
12
12
  }
13
13
  ```
14
14
 
15
- Using `@latest` ensures you always get the newest version when the cache is refreshed.
15
+ Restart OpenCode. Done.
16
16
 
17
- To pin a specific version:
17
+ ## What it does
18
18
 
19
- ```json
20
- {
21
- "plugin": ["@mohak34/opencode-notifier@0.1.14"]
22
- }
23
- ```
19
+ You'll get notified when:
20
+ - OpenCode needs permission to run something
21
+ - Your session finishes
22
+ - An error happens
23
+ - The question tool pops up
24
24
 
25
- Restart OpenCode. The plugin will be automatically installed and loaded.
25
+ There's also `subagent_complete` for when subagents finish, but that's silent by default so you don't get spammed.
26
26
 
27
- ## Updating
28
-
29
- OpenCode caches plugins in `~/.cache/opencode`. Plugins are not auto-updated; you need to clear the cache to get new versions.
27
+ ## Setup by platform
30
28
 
31
- ### If you use `@latest`
29
+ **macOS**: Nothing to do, works out of the box. Shows the Script Editor icon.
32
30
 
33
- Clear the cache and restart OpenCode:
34
-
35
- **Linux/macOS:**
31
+ **Linux**: Should work if you already have a notification system setup. If not install libnotify:
36
32
 
37
33
  ```bash
38
- rm -rf ~/.cache/opencode/node_modules/@mohak34/opencode-notifier
34
+ sudo apt install libnotify-bin # Ubuntu/Debian
35
+ sudo dnf install libnotify # Fedora
36
+ sudo pacman -S libnotify # Arch
39
37
  ```
40
38
 
41
- **Windows (PowerShell):**
42
-
43
- ```powershell
44
- Remove-Item -Recurse -Force "$env:USERPROFILE\.cache\opencode\node_modules\@mohak34\opencode-notifier"
45
- ```
46
-
47
- Then restart OpenCode - it will download the latest version automatically.
48
-
49
- ### If you use a pinned version (e.g., `@0.1.14`)
50
-
51
- 1. Update the version in your `opencode.json`:
52
-
53
- ```json
54
- {
55
- "plugin": ["@mohak34/opencode-notifier@0.1.14"]
56
- }
57
- ```
58
-
59
- 2. Clear the cache (see commands above)
60
-
61
- 3. Restart OpenCode
62
-
63
- ### Check installed version
64
-
65
- **Linux/macOS:**
66
-
67
- ```bash
68
- cat ~/.cache/opencode/node_modules/@mohak34/opencode-notifier/package.json | grep version
69
- ```
70
-
71
- **Windows (PowerShell):**
72
-
73
- ```powershell
74
- Get-Content "$env:USERPROFILE\.cache\opencode\node_modules\@mohak34\opencode-notifier\package.json" | Select-String "version"
75
- ```
76
-
77
- ## Platform Notes
78
-
79
- The plugin works out of the box on all platforms. For best results:
80
-
81
- - **macOS**: No additional setup required. Notifications display the Script Editor icon (custom icons not supported with osascript for reliability)
82
- - **Windows**: No additional setup required. The OpenCode icon displays in notifications
83
- - **Linux**: For sounds, one of these should be installed: `paplay`, `aplay`, `mpv`, or `ffplay`. For notifications, `notify-send` is recommended. The OpenCode icon displays in notifications
84
-
85
- **Note**: To disable icons, set `showIcon: false` in your configuration.
86
-
87
- ### macOS: Choosing Your Notification System
88
-
89
- On macOS, you can choose between two notification backends:
39
+ For sounds, you need one of: `paplay`, `aplay`, `mpv`, or `ffplay`
90
40
 
91
- - **`osascript`** (default): Most reliable method that always works. Uses AppleScript to display notifications. **Limitation**: Cannot display custom icons (shows Script Editor icon instead).
41
+ **Windows**: Works out of the box. But heads up:
42
+ - Only `.wav` files work (not mp3)
43
+ - Use full paths like `C:/Users/You/sounds/alert.wav` not `~/`
92
44
 
93
- - **`node-notifier`**: Supports custom icons but some users experience reliability issues where notifications don't appear.
45
+ ## Config file
94
46
 
95
- To use `node-notifier` with custom icons:
96
-
97
- ```json
98
- {
99
- "notificationSystem": "node-notifier"
100
- }
101
- ```
102
-
103
- If you experience missing notifications with `node-notifier`, switch back to `osascript` (or remove the option to use the default).
104
-
105
- ## Configuration
106
-
107
- To customize the plugin, create `~/.config/opencode/opencode-notifier.json`:
47
+ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
108
48
 
109
49
  ```json
110
50
  {
@@ -135,204 +75,267 @@ To customize the plugin, create `~/.config/opencode/opencode-notifier.json`:
135
75
  "question": "Session has a question"
136
76
  },
137
77
  "sounds": {
138
- "permission": "/path/to/custom/sound.wav",
139
- "complete": "/path/to/custom/sound.wav",
140
- "subagent_complete": "/path/to/custom/sound.wav",
141
- "error": "/path/to/custom/sound.wav",
142
- "question": "/path/to/custom/sound.wav"
78
+ "permission": null,
79
+ "complete": null,
80
+ "subagent_complete": null,
81
+ "error": null,
82
+ "question": null
83
+ },
84
+ "volumes": {
85
+ "permission": 1,
86
+ "complete": 1,
87
+ "subagent_complete": 1,
88
+ "error": 1,
89
+ "question": 1
143
90
  }
144
91
  }
145
92
  ```
146
93
 
147
- ### Options
94
+ ## All options
148
95
 
149
- | Option | Type | Default | Description |
150
- |--------|------|---------|-------------|
151
- | `sound` | boolean | `true` | Global toggle for all sounds |
152
- | `notification` | boolean | `true` | Global toggle for all notifications |
153
- | `timeout` | number | `5` | Notification duration in seconds (Linux only) |
154
- | `showProjectName` | boolean | `true` | Show project folder name in notification title |
155
- | `showIcon` | boolean | `true` | Show OpenCode icon in notifications |
156
- | `notificationSystem` | string | `"osascript"` | macOS only: `"osascript"` (reliable, no icons) or `"node-notifier"` (icons, may have issues) |
157
- | `command` | object | — | Command execution settings (enabled/path/args/minDuration) |
96
+ ### Global options
97
+
98
+ ```json
99
+ {
100
+ "sound": true,
101
+ "notification": true,
102
+ "timeout": 5,
103
+ "showProjectName": true,
104
+ "showIcon": true,
105
+ "notificationSystem": "osascript"
106
+ }
107
+ ```
108
+
109
+ - `sound` - Turn sounds on/off (default: true)
110
+ - `notification` - Turn notifications on/off (default: true)
111
+ - `timeout` - How long notifications show in seconds, Linux only (default: 5)
112
+ - `showProjectName` - Show folder name in notification title (default: true)
113
+ - `showIcon` - Show OpenCode icon, Windows/Linux only (default: true)
114
+ - `notificationSystem` - macOS only: `"osascript"` or `"node-notifier"` (default: "osascript")
158
115
 
159
116
  ### Events
160
117
 
161
- Control sound and notification separately for each event:
118
+ Control each event separately:
162
119
 
163
120
  ```json
164
121
  {
165
122
  "events": {
166
123
  "permission": { "sound": true, "notification": true },
167
- "complete": { "sound": false, "notification": true },
168
- "subagent_complete": { "sound": true, "notification": false },
169
- "error": { "sound": true, "notification": false },
124
+ "complete": { "sound": true, "notification": true },
125
+ "subagent_complete": { "sound": false, "notification": false },
126
+ "error": { "sound": true, "notification": true },
170
127
  "question": { "sound": true, "notification": true }
171
128
  }
172
129
  }
173
130
  ```
174
131
 
175
- Or use a boolean to toggle both:
132
+ Or use true/false for both:
176
133
 
177
134
  ```json
178
135
  {
179
136
  "events": {
180
- "permission": true,
181
- "complete": false,
182
- "subagent_complete": true,
183
- "error": true,
184
- "question": true
137
+ "complete": false
185
138
  }
186
139
  }
187
140
  ```
188
141
 
189
- Note: `complete` fires for primary (main) session completion, while `subagent_complete` fires for subagent completion. `subagent_complete` defaults to disabled (both sound and notification are false).
190
-
191
142
  ### Messages
192
143
 
193
- Customize notification text:
144
+ Customize the notification text:
194
145
 
195
146
  ```json
196
147
  {
197
148
  "messages": {
198
- "permission": "Action required",
199
- "complete": "Done!",
200
- "subagent_complete": "Subagent finished",
201
- "error": "Something went wrong",
202
- "question": "Input needed"
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"
203
154
  }
204
155
  }
205
156
  ```
206
157
 
207
- ### Command
208
-
209
- Run a custom command when events fire. Use `{event}` and `{message}` tokens in `path` or `args` to inject the event name and message.
158
+ ### Sounds
210
159
 
211
- `command.minDuration` (optional, seconds) gates command execution based on the time elapsed since the last user prompt.
212
- - Applies to all events for the custom command.
213
- - If elapsed time is known and is below `minDuration`, the command is skipped.
214
- - If elapsed time cannot be determined for an event, the command still runs.
160
+ Use your own sound files:
215
161
 
216
162
  ```json
217
163
  {
218
- "command": {
219
- "enabled": true,
220
- "path": "/path/to/command",
221
- "args": ["--event", "{event}", "--message", "{message}"],
222
- "minDuration": 10
164
+ "sounds": {
165
+ "permission": "/path/to/alert.wav",
166
+ "complete": "/path/to/done.wav",
167
+ "subagent_complete": "/path/to/subagent-done.wav",
168
+ "error": "/path/to/error.wav",
169
+ "question": "/path/to/question.wav"
223
170
  }
224
171
  }
225
172
  ```
226
173
 
227
- ### Custom Sounds
174
+ Platform notes:
175
+ - macOS/Linux: .wav or .mp3 files work
176
+ - Windows: Only .wav files work
177
+ - If file doesn't exist, falls back to bundled sound
228
178
 
229
- Use your own sound files:
179
+ ### Volumes
180
+
181
+ Set per-event volume from `0` to `1`:
230
182
 
231
183
  ```json
232
184
  {
233
- "sounds": {
234
- "permission": "/home/user/sounds/alert.wav",
235
- "complete": "/home/user/sounds/done.wav",
236
- "subagent_complete": "/home/user/sounds/subagent-done.wav",
237
- "error": "/home/user/sounds/error.wav",
238
- "question": "/home/user/sounds/question.wav"
185
+ "volumes": {
186
+ "permission": 0.6,
187
+ "complete": 0.3,
188
+ "subagent_complete": 0.15,
189
+ "error": 1,
190
+ "question": 0.7
239
191
  }
240
192
  }
241
193
  ```
242
194
 
243
- If a custom sound file path is provided but the file doesn't exist, the plugin will fall back to the bundled sound.
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
244
198
 
245
- ### Example: Different behaviors for main and subagent completion
199
+ ### Custom commands
246
200
 
247
- You may want different notification behaviors for primary sessions versus subagent sessions. For example:
201
+ Run your own script when something happens. Use `{event}` and `{message}` as placeholders:
248
202
 
249
- - **Main session completion**: Play a sound and show a system notification
250
- - **Subagent completion**: Play a different sound, but no system notification
203
+ ```json
204
+ {
205
+ "command": {
206
+ "enabled": true,
207
+ "path": "/path/to/your/script",
208
+ "args": ["{event}", "{message}"],
209
+ "minDuration": 10
210
+ }
211
+ }
212
+ ```
213
+
214
+ - `enabled` - Turn command on/off
215
+ - `path` - Path to your script/executable
216
+ - `args` - Arguments to pass, can use `{event}` and `{message}` tokens
217
+ - `minDuration` - Skip if response was quick, avoids spam (seconds)
218
+
219
+ #### Example: Log events to a file
251
220
 
252
221
  ```json
253
222
  {
254
- "events": {
255
- "complete": { "sound": true, "notification": true },
256
- "subagent_complete": { "sound": true, "notification": false }
257
- },
258
- "messages": {
259
- "complete": "Session has finished",
260
- "subagent_complete": "Subagent task completed"
261
- },
262
- "sounds": {
263
- "complete": "/home/user/sounds/main-done.wav",
264
- "subagent_complete": "/home/user/sounds/subagent-chime.wav"
223
+ "command": {
224
+ "enabled": true,
225
+ "path": "/bin/bash",
226
+ "args": [
227
+ "-c",
228
+ "echo '[{event}] {message}' >> /tmp/opencode.log"
229
+ ]
265
230
  }
266
231
  }
267
232
  ```
268
233
 
269
- ## Troubleshooting
234
+ ## macOS: Pick your notification style
270
235
 
271
- ### macOS: Notifications not showing (only sound works)
236
+ **osascript** (default): Reliable but shows Script Editor icon
272
237
 
273
- **Update to v0.1.10 or later** - this version includes a fix for macOS notification events.
238
+ ```json
239
+ {
240
+ "notificationSystem": "osascript"
241
+ }
242
+ ```
274
243
 
275
- If notifications still don't work after updating:
244
+ **node-notifier**: Shows OpenCode icon but might miss notifications sometimes
276
245
 
277
- 1. **Check notification permissions:**
278
- - Open **System Settings > Notifications**
279
- - Find **Script Editor** in the list
280
- - Make sure notifications are set to **Banners** or **Alerts**
246
+ ```json
247
+ {
248
+ "notificationSystem": "node-notifier"
249
+ }
250
+ ```
281
251
 
282
- ### Linux: Notifications not showing
252
+ **NOTE:** If you go with node-notifier and start missing notifications, just switch back or remove the option from the config. Users have reported issues with using node-notifier for receiving only sounds and no notification popups.
283
253
 
284
- 1. **Install notify-send:**
254
+ ## Updating
285
255
 
286
- ```bash
287
- # Debian/Ubuntu
288
- sudo apt install libnotify-bin
256
+ If Opencode does not update the plugin or there is an issue with the cache version:
289
257
 
290
- # Fedora
291
- sudo dnf install libnotify
258
+ ```bash
259
+ # Linux/macOS
260
+ rm -rf ~/.cache/opencode/node_modules/@mohak34/opencode-notifier
292
261
 
293
- # Arch
294
- sudo pacman -S libnotify
295
- ```
262
+ # Windows
263
+ Remove-Item -Recurse -Force "$env:USERPROFILE\.cache\opencode\node_modules\@mohak34\opencode-notifier"
264
+ ```
296
265
 
297
- 2. **Test if it works:**
266
+ Then restart OpenCode.
298
267
 
299
- ```bash
300
- notify-send "Test" "Hello"
301
- ```
268
+ ## Troubleshooting
302
269
 
303
- ### Linux: Sounds not playing
270
+ **macOS: Not seeing notifications?**
271
+ Go to System Settings > Notifications > Script Editor, make sure it's set to Banners or Alerts.
304
272
 
305
- Install one of these audio players: `paplay`, `aplay`, `mpv`, or `ffplay`.
273
+ **macOS: node-notifier not showing notifications?**
274
+ Switch back to osascript. Some users report node-notifier works for sounds but not visual notifications on certain macOS versions.
306
275
 
276
+ **Linux: No notifications?**
277
+ Install libnotify-bin:
307
278
  ```bash
308
- # Debian/Ubuntu (PulseAudio)
309
- sudo apt install pulseaudio-utils
310
-
311
- # Or install mpv
312
- sudo apt install mpv
279
+ sudo apt install libnotify-bin # Debian/Ubuntu
280
+ sudo dnf install libnotify # Fedora
281
+ sudo pacman -S libnotify # Arch
313
282
  ```
314
283
 
315
- ### Windows: Notifications not showing
284
+ Test with: `notify-send "Test" "Hello"`
285
+
286
+ **Linux: No sounds?**
287
+ Install one of: `paplay`, `aplay`, `mpv`, or `ffplay`
316
288
 
317
- 1. Open **Settings > System > Notifications**
318
- 2. Make sure notifications are enabled
319
- 3. Check that your terminal app has notification permissions
289
+ **Windows: Custom sounds not working?**
290
+ - Must be .wav format (not .mp3)
291
+ - Use full Windows paths: `C:/Users/YourName/sounds/alert.wav` (not `~/`)
292
+ - Make sure the file actually plays in Windows Media Player
293
+ - If using WSL, the path should be accessible from Windows
294
+
295
+ **Windows WSL notifications not working?**
296
+ WSL doesn't have a native notification daemon. Use PowerShell commands instead:
297
+
298
+ ```json
299
+ {
300
+ "notification": false,
301
+ "sound": true,
302
+ "command": {
303
+ "enabled": true,
304
+ "path": "powershell.exe",
305
+ "args": [
306
+ "-Command",
307
+ "$wshell = New-Object -ComObject Wscript.Shell; $wshell.Popup('{message}', 5, 'OpenCode - {event}', 0+64)"
308
+ ]
309
+ }
310
+ }
311
+ ```
320
312
 
321
- ### General: Plugin not loading
313
+ **Windows: OpenCode crashes when notifications appear?**
314
+ This is a known Bun issue on Windows. Disable native notifications and use PowerShell popups:
322
315
 
323
- 1. **Check your opencode.json syntax:**
316
+ ```json
317
+ {
318
+ "notification": false,
319
+ "sound": true,
320
+ "command": {
321
+ "enabled": true,
322
+ "path": "powershell.exe",
323
+ "args": [
324
+ "-Command",
325
+ "$wshell = New-Object -ComObject Wscript.Shell; $wshell.Popup('{message}', 5, 'OpenCode - {event}', 0+64)"
326
+ ]
327
+ }
328
+ }
329
+ ```
324
330
 
325
- ```json
326
- {
327
- "plugin": ["@mohak34/opencode-notifier@latest"]
328
- }
329
- ```
331
+ **Plugin not loading?**
332
+ - Check your opencode.json syntax
333
+ - Clear the cache (see Updating section)
334
+ - Restart OpenCode
330
335
 
331
- 2. **Clear the cache and restart:**
336
+ ## Changelog
332
337
 
333
- ```bash
334
- rm -rf ~/.cache/opencode/node_modules/@mohak34/opencode-notifier
335
- ```
338
+ See [CHANGELOG.md](CHANGELOG.md)
336
339
 
337
340
  ## License
338
341
 
package/dist/index.js CHANGED
@@ -3770,6 +3770,13 @@ var DEFAULT_CONFIG = {
3770
3770
  subagent_complete: null,
3771
3771
  error: null,
3772
3772
  question: null
3773
+ },
3774
+ volumes: {
3775
+ permission: 1,
3776
+ complete: 1,
3777
+ subagent_complete: 1,
3778
+ error: 1,
3779
+ question: 1
3773
3780
  }
3774
3781
  };
3775
3782
  function getConfigPath() {
@@ -3790,6 +3797,18 @@ function parseEventConfig(userEvent, defaultConfig) {
3790
3797
  notification: userEvent.notification ?? defaultConfig.notification
3791
3798
  };
3792
3799
  }
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
+ }
3793
3812
  function loadConfig() {
3794
3813
  const configPath = getConfigPath();
3795
3814
  if (!existsSync(configPath)) {
@@ -3840,6 +3859,13 @@ function loadConfig() {
3840
3859
  subagent_complete: userConfig.sounds?.subagent_complete ?? DEFAULT_CONFIG.sounds.subagent_complete,
3841
3860
  error: userConfig.sounds?.error ?? DEFAULT_CONFIG.sounds.error,
3842
3861
  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)
3843
3869
  }
3844
3870
  };
3845
3871
  } catch {
@@ -3858,6 +3884,9 @@ function getMessage(config, event) {
3858
3884
  function getSoundPath(config, event) {
3859
3885
  return config.sounds[event];
3860
3886
  }
3887
+ function getSoundVolume(config, event) {
3888
+ return config.volumes[event];
3889
+ }
3861
3890
  function getIconPath(config) {
3862
3891
  if (!config.showIcon) {
3863
3892
  return;
@@ -3939,6 +3968,8 @@ import { existsSync as existsSync2 } from "fs";
3939
3968
  import { spawn } from "child_process";
3940
3969
  var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
3941
3970
  var DEBOUNCE_MS2 = 1000;
3971
+ var FULL_VOLUME_PERCENT = 100;
3972
+ var FULL_VOLUME_PULSE = 65536;
3942
3973
  var lastSoundTime = {};
3943
3974
  function getBundledSoundPath(event) {
3944
3975
  const soundFilename = `${event}.wav`;
@@ -3981,12 +4012,32 @@ async function runCommand(command, args) {
3981
4012
  });
3982
4013
  });
3983
4014
  }
3984
- async function playOnLinux(soundPath) {
4015
+ function normalizeVolume(volume) {
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);
3985
4036
  const players = [
3986
- { command: "paplay", args: [soundPath] },
4037
+ { command: "paplay", args: [`--volume=${pulseVolume}`, soundPath] },
3987
4038
  { command: "aplay", args: [soundPath] },
3988
- { command: "mpv", args: ["--no-video", "--no-terminal", soundPath] },
3989
- { command: "ffplay", args: ["-nodisp", "-autoexit", "-loglevel", "quiet", soundPath] }
4039
+ { command: "mpv", args: ["--no-video", "--no-terminal", `--volume=${percentVolume}`, soundPath] },
4040
+ { command: "ffplay", args: ["-nodisp", "-autoexit", "-loglevel", "quiet", "-volume", `${percentVolume}`, soundPath] }
3990
4041
  ];
3991
4042
  for (const player of players) {
3992
4043
  try {
@@ -3997,20 +4048,21 @@ async function playOnLinux(soundPath) {
3997
4048
  }
3998
4049
  }
3999
4050
  }
4000
- async function playOnMac(soundPath) {
4001
- await runCommand("afplay", [soundPath]);
4051
+ async function playOnMac(soundPath, volume) {
4052
+ await runCommand("afplay", ["-v", `${volume}`, soundPath]);
4002
4053
  }
4003
4054
  async function playOnWindows(soundPath) {
4004
4055
  const script = `& { (New-Object Media.SoundPlayer $args[0]).PlaySync() }`;
4005
4056
  await runCommand("powershell", ["-c", script, soundPath]);
4006
4057
  }
4007
- async function playSound(event, customPath) {
4058
+ async function playSound(event, customPath, volume) {
4008
4059
  const now = Date.now();
4009
4060
  if (lastSoundTime[event] && now - lastSoundTime[event] < DEBOUNCE_MS2) {
4010
4061
  return;
4011
4062
  }
4012
4063
  lastSoundTime[event] = now;
4013
4064
  const soundPath = getSoundFilePath(event, customPath);
4065
+ const normalizedVolume = normalizeVolume(volume);
4014
4066
  if (!soundPath) {
4015
4067
  return;
4016
4068
  }
@@ -4018,10 +4070,10 @@ async function playSound(event, customPath) {
4018
4070
  try {
4019
4071
  switch (os2) {
4020
4072
  case "darwin":
4021
- await playOnMac(soundPath);
4073
+ await playOnMac(soundPath, normalizedVolume);
4022
4074
  break;
4023
4075
  case "linux":
4024
- await playOnLinux(soundPath);
4076
+ await playOnLinux(soundPath, normalizedVolume);
4025
4077
  break;
4026
4078
  case "win32":
4027
4079
  await playOnWindows(soundPath);
@@ -4068,7 +4120,8 @@ async function handleEvent(config, eventType, projectName, elapsedSeconds) {
4068
4120
  }
4069
4121
  if (isEventSoundEnabled(config, eventType)) {
4070
4122
  const customSoundPath = getSoundPath(config, eventType);
4071
- promises.push(playSound(eventType, customSoundPath));
4123
+ const soundVolume = getSoundVolume(config, eventType);
4124
+ promises.push(playSound(eventType, customSoundPath, soundVolume));
4072
4125
  }
4073
4126
  const minDuration = config.command?.minDuration;
4074
4127
  const shouldSkipCommand = typeof minDuration === "number" && Number.isFinite(minDuration) && minDuration > 0 && typeof elapsedSeconds === "number" && Number.isFinite(elapsedSeconds) && elapsedSeconds < minDuration;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohak34/opencode-notifier",
3
- "version": "0.1.19-beta.0",
3
+ "version": "0.1.20-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",