@mohak34/opencode-notifier 0.1.21-beta.0 → 0.1.21
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 +29 -2
- package/dist/index.js +64 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,7 +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":
|
|
55
|
+
"showSessionTitle": false,
|
|
56
56
|
"showIcon": true,
|
|
57
57
|
"notificationSystem": "osascript",
|
|
58
58
|
"command": {
|
|
@@ -81,6 +81,13 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
|
|
|
81
81
|
"subagent_complete": null,
|
|
82
82
|
"error": null,
|
|
83
83
|
"question": null
|
|
84
|
+
},
|
|
85
|
+
"volumes": {
|
|
86
|
+
"permission": 1,
|
|
87
|
+
"complete": 1,
|
|
88
|
+
"subagent_complete": 1,
|
|
89
|
+
"error": 1,
|
|
90
|
+
"question": 1
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
```
|
|
@@ -95,7 +102,7 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
|
|
|
95
102
|
"notification": true,
|
|
96
103
|
"timeout": 5,
|
|
97
104
|
"showProjectName": true,
|
|
98
|
-
"showSessionTitle":
|
|
105
|
+
"showSessionTitle": false,
|
|
99
106
|
"showIcon": true,
|
|
100
107
|
"notificationSystem": "osascript"
|
|
101
108
|
}
|
|
@@ -181,6 +188,26 @@ Platform notes:
|
|
|
181
188
|
- Windows: Only .wav files work
|
|
182
189
|
- If file doesn't exist, falls back to bundled sound
|
|
183
190
|
|
|
191
|
+
### Volumes
|
|
192
|
+
|
|
193
|
+
Set per-event volume from `0` to `1`:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"volumes": {
|
|
198
|
+
"permission": 0.6,
|
|
199
|
+
"complete": 0.3,
|
|
200
|
+
"subagent_complete": 0.15,
|
|
201
|
+
"error": 1,
|
|
202
|
+
"question": 0.7
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
- `0` = mute, `1` = full volume
|
|
208
|
+
- Values outside `0..1` are clamped automatically
|
|
209
|
+
- On Windows, playback still works but custom volume may not be honored by the default player
|
|
210
|
+
|
|
184
211
|
### Custom commands
|
|
185
212
|
|
|
186
213
|
Run your own script when something happens. Use `{event}`, `{message}`, and `{sessionTitle}` as placeholders:
|
package/dist/index.js
CHANGED
|
@@ -3743,7 +3743,7 @@ var DEFAULT_CONFIG = {
|
|
|
3743
3743
|
notification: true,
|
|
3744
3744
|
timeout: 5,
|
|
3745
3745
|
showProjectName: true,
|
|
3746
|
-
showSessionTitle:
|
|
3746
|
+
showSessionTitle: false,
|
|
3747
3747
|
showIcon: true,
|
|
3748
3748
|
notificationSystem: "osascript",
|
|
3749
3749
|
command: {
|
|
@@ -3771,6 +3771,13 @@ var DEFAULT_CONFIG = {
|
|
|
3771
3771
|
subagent_complete: null,
|
|
3772
3772
|
error: null,
|
|
3773
3773
|
question: null
|
|
3774
|
+
},
|
|
3775
|
+
volumes: {
|
|
3776
|
+
permission: 1,
|
|
3777
|
+
complete: 1,
|
|
3778
|
+
subagent_complete: 1,
|
|
3779
|
+
error: 1,
|
|
3780
|
+
question: 1
|
|
3774
3781
|
}
|
|
3775
3782
|
};
|
|
3776
3783
|
function getConfigPath() {
|
|
@@ -3791,6 +3798,18 @@ function parseEventConfig(userEvent, defaultConfig) {
|
|
|
3791
3798
|
notification: userEvent.notification ?? defaultConfig.notification
|
|
3792
3799
|
};
|
|
3793
3800
|
}
|
|
3801
|
+
function parseVolume(value, defaultVolume) {
|
|
3802
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
3803
|
+
return defaultVolume;
|
|
3804
|
+
}
|
|
3805
|
+
if (value < 0) {
|
|
3806
|
+
return 0;
|
|
3807
|
+
}
|
|
3808
|
+
if (value > 1) {
|
|
3809
|
+
return 1;
|
|
3810
|
+
}
|
|
3811
|
+
return value;
|
|
3812
|
+
}
|
|
3794
3813
|
function loadConfig() {
|
|
3795
3814
|
const configPath = getConfigPath();
|
|
3796
3815
|
if (!existsSync(configPath)) {
|
|
@@ -3842,6 +3861,13 @@ function loadConfig() {
|
|
|
3842
3861
|
subagent_complete: userConfig.sounds?.subagent_complete ?? DEFAULT_CONFIG.sounds.subagent_complete,
|
|
3843
3862
|
error: userConfig.sounds?.error ?? DEFAULT_CONFIG.sounds.error,
|
|
3844
3863
|
question: userConfig.sounds?.question ?? DEFAULT_CONFIG.sounds.question
|
|
3864
|
+
},
|
|
3865
|
+
volumes: {
|
|
3866
|
+
permission: parseVolume(userConfig.volumes?.permission, DEFAULT_CONFIG.volumes.permission),
|
|
3867
|
+
complete: parseVolume(userConfig.volumes?.complete, DEFAULT_CONFIG.volumes.complete),
|
|
3868
|
+
subagent_complete: parseVolume(userConfig.volumes?.subagent_complete, DEFAULT_CONFIG.volumes.subagent_complete),
|
|
3869
|
+
error: parseVolume(userConfig.volumes?.error, DEFAULT_CONFIG.volumes.error),
|
|
3870
|
+
question: parseVolume(userConfig.volumes?.question, DEFAULT_CONFIG.volumes.question)
|
|
3845
3871
|
}
|
|
3846
3872
|
};
|
|
3847
3873
|
} catch {
|
|
@@ -3860,6 +3886,9 @@ function getMessage(config, event) {
|
|
|
3860
3886
|
function getSoundPath(config, event) {
|
|
3861
3887
|
return config.sounds[event];
|
|
3862
3888
|
}
|
|
3889
|
+
function getSoundVolume(config, event) {
|
|
3890
|
+
return config.volumes[event];
|
|
3891
|
+
}
|
|
3863
3892
|
function getIconPath(config) {
|
|
3864
3893
|
if (!config.showIcon) {
|
|
3865
3894
|
return;
|
|
@@ -3951,6 +3980,8 @@ import { existsSync as existsSync2 } from "fs";
|
|
|
3951
3980
|
import { spawn } from "child_process";
|
|
3952
3981
|
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
3953
3982
|
var DEBOUNCE_MS2 = 1000;
|
|
3983
|
+
var FULL_VOLUME_PERCENT = 100;
|
|
3984
|
+
var FULL_VOLUME_PULSE = 65536;
|
|
3954
3985
|
var lastSoundTime = {};
|
|
3955
3986
|
function getBundledSoundPath(event) {
|
|
3956
3987
|
const soundFilename = `${event}.wav`;
|
|
@@ -3993,12 +4024,32 @@ async function runCommand(command, args) {
|
|
|
3993
4024
|
});
|
|
3994
4025
|
});
|
|
3995
4026
|
}
|
|
3996
|
-
|
|
4027
|
+
function normalizeVolume(volume) {
|
|
4028
|
+
if (!Number.isFinite(volume)) {
|
|
4029
|
+
return 1;
|
|
4030
|
+
}
|
|
4031
|
+
if (volume < 0) {
|
|
4032
|
+
return 0;
|
|
4033
|
+
}
|
|
4034
|
+
if (volume > 1) {
|
|
4035
|
+
return 1;
|
|
4036
|
+
}
|
|
4037
|
+
return volume;
|
|
4038
|
+
}
|
|
4039
|
+
function toPercentVolume(volume) {
|
|
4040
|
+
return Math.round(volume * FULL_VOLUME_PERCENT);
|
|
4041
|
+
}
|
|
4042
|
+
function toPulseVolume(volume) {
|
|
4043
|
+
return Math.round(volume * FULL_VOLUME_PULSE);
|
|
4044
|
+
}
|
|
4045
|
+
async function playOnLinux(soundPath, volume) {
|
|
4046
|
+
const percentVolume = toPercentVolume(volume);
|
|
4047
|
+
const pulseVolume = toPulseVolume(volume);
|
|
3997
4048
|
const players = [
|
|
3998
|
-
{ command: "paplay", args: [soundPath] },
|
|
4049
|
+
{ command: "paplay", args: [`--volume=${pulseVolume}`, soundPath] },
|
|
3999
4050
|
{ command: "aplay", args: [soundPath] },
|
|
4000
|
-
{ command: "mpv", args: ["--no-video", "--no-terminal", soundPath] },
|
|
4001
|
-
{ command: "ffplay", args: ["-nodisp", "-autoexit", "-loglevel", "quiet", soundPath] }
|
|
4051
|
+
{ command: "mpv", args: ["--no-video", "--no-terminal", `--volume=${percentVolume}`, soundPath] },
|
|
4052
|
+
{ command: "ffplay", args: ["-nodisp", "-autoexit", "-loglevel", "quiet", "-volume", `${percentVolume}`, soundPath] }
|
|
4002
4053
|
];
|
|
4003
4054
|
for (const player of players) {
|
|
4004
4055
|
try {
|
|
@@ -4009,20 +4060,21 @@ async function playOnLinux(soundPath) {
|
|
|
4009
4060
|
}
|
|
4010
4061
|
}
|
|
4011
4062
|
}
|
|
4012
|
-
async function playOnMac(soundPath) {
|
|
4013
|
-
await runCommand("afplay", [soundPath]);
|
|
4063
|
+
async function playOnMac(soundPath, volume) {
|
|
4064
|
+
await runCommand("afplay", ["-v", `${volume}`, soundPath]);
|
|
4014
4065
|
}
|
|
4015
4066
|
async function playOnWindows(soundPath) {
|
|
4016
4067
|
const script = `& { (New-Object Media.SoundPlayer $args[0]).PlaySync() }`;
|
|
4017
4068
|
await runCommand("powershell", ["-c", script, soundPath]);
|
|
4018
4069
|
}
|
|
4019
|
-
async function playSound(event, customPath) {
|
|
4070
|
+
async function playSound(event, customPath, volume) {
|
|
4020
4071
|
const now = Date.now();
|
|
4021
4072
|
if (lastSoundTime[event] && now - lastSoundTime[event] < DEBOUNCE_MS2) {
|
|
4022
4073
|
return;
|
|
4023
4074
|
}
|
|
4024
4075
|
lastSoundTime[event] = now;
|
|
4025
4076
|
const soundPath = getSoundFilePath(event, customPath);
|
|
4077
|
+
const normalizedVolume = normalizeVolume(volume);
|
|
4026
4078
|
if (!soundPath) {
|
|
4027
4079
|
return;
|
|
4028
4080
|
}
|
|
@@ -4030,10 +4082,10 @@ async function playSound(event, customPath) {
|
|
|
4030
4082
|
try {
|
|
4031
4083
|
switch (os2) {
|
|
4032
4084
|
case "darwin":
|
|
4033
|
-
await playOnMac(soundPath);
|
|
4085
|
+
await playOnMac(soundPath, normalizedVolume);
|
|
4034
4086
|
break;
|
|
4035
4087
|
case "linux":
|
|
4036
|
-
await playOnLinux(soundPath);
|
|
4088
|
+
await playOnLinux(soundPath, normalizedVolume);
|
|
4037
4089
|
break;
|
|
4038
4090
|
case "win32":
|
|
4039
4091
|
await playOnWindows(soundPath);
|
|
@@ -4087,7 +4139,8 @@ async function handleEvent(config, eventType, projectName, elapsedSeconds, sessi
|
|
|
4087
4139
|
}
|
|
4088
4140
|
if (isEventSoundEnabled(config, eventType)) {
|
|
4089
4141
|
const customSoundPath = getSoundPath(config, eventType);
|
|
4090
|
-
|
|
4142
|
+
const soundVolume = getSoundVolume(config, eventType);
|
|
4143
|
+
promises.push(playSound(eventType, customSoundPath, soundVolume));
|
|
4091
4144
|
}
|
|
4092
4145
|
const minDuration = config.command?.minDuration;
|
|
4093
4146
|
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.21
|
|
3
|
+
"version": "0.1.21",
|
|
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",
|