@mohak34/opencode-notifier 0.1.29-beta.0 → 0.1.30-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 +47 -9
- package/dist/index.js +227 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ You'll get notified when:
|
|
|
22
22
|
- An error happens
|
|
23
23
|
- The question tool pops up
|
|
24
24
|
|
|
25
|
-
There's also `subagent_complete` for when subagents finish,
|
|
25
|
+
There's also `subagent_complete` for when subagents finish, and `user_cancelled` for when you press ESC to abort -- both are silent by default so you don't get spammed.
|
|
26
26
|
|
|
27
27
|
## Setup by platform
|
|
28
28
|
|
|
@@ -54,6 +54,7 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
|
|
|
54
54
|
"showProjectName": true,
|
|
55
55
|
"showSessionTitle": false,
|
|
56
56
|
"showIcon": true,
|
|
57
|
+
"suppressWhenFocused": true,
|
|
57
58
|
"notificationSystem": "osascript",
|
|
58
59
|
"linux": {
|
|
59
60
|
"grouping": false
|
|
@@ -69,28 +70,32 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
|
|
|
69
70
|
"complete": { "sound": true, "notification": true },
|
|
70
71
|
"subagent_complete": { "sound": false, "notification": false },
|
|
71
72
|
"error": { "sound": true, "notification": true },
|
|
72
|
-
"question": { "sound": true, "notification": true }
|
|
73
|
+
"question": { "sound": true, "notification": true },
|
|
74
|
+
"user_cancelled": { "sound": false, "notification": false }
|
|
73
75
|
},
|
|
74
76
|
"messages": {
|
|
75
77
|
"permission": "Session needs permission: {sessionTitle}",
|
|
76
78
|
"complete": "Session has finished: {sessionTitle}",
|
|
77
79
|
"subagent_complete": "Subagent task completed: {sessionTitle}",
|
|
78
80
|
"error": "Session encountered an error: {sessionTitle}",
|
|
79
|
-
"question": "Session has a question: {sessionTitle}"
|
|
81
|
+
"question": "Session has a question: {sessionTitle}",
|
|
82
|
+
"user_cancelled": "Session was cancelled by user: {sessionTitle}"
|
|
80
83
|
},
|
|
81
84
|
"sounds": {
|
|
82
85
|
"permission": null,
|
|
83
86
|
"complete": null,
|
|
84
87
|
"subagent_complete": null,
|
|
85
88
|
"error": null,
|
|
86
|
-
"question": null
|
|
89
|
+
"question": null,
|
|
90
|
+
"user_cancelled": null
|
|
87
91
|
},
|
|
88
92
|
"volumes": {
|
|
89
93
|
"permission": 1,
|
|
90
94
|
"complete": 1,
|
|
91
95
|
"subagent_complete": 1,
|
|
92
96
|
"error": 1,
|
|
93
|
-
"question": 1
|
|
97
|
+
"question": 1,
|
|
98
|
+
"user_cancelled": 1
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
```
|
|
@@ -117,6 +122,7 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
|
|
|
117
122
|
- `showProjectName` - Show folder name in notification title (default: true)
|
|
118
123
|
- `showSessionTitle` - Include the session title in notification messages via `{sessionTitle}` placeholder (default: true)
|
|
119
124
|
- `showIcon` - Show OpenCode icon, Windows/Linux only (default: true)
|
|
125
|
+
- `suppressWhenFocused` - Skip notifications and sounds when the terminal is the active window (default: true). See [Focus detection](#focus-detection) for platform details
|
|
120
126
|
- `notificationSystem` - macOS only: `"osascript"` or `"node-notifier"` (default: "osascript")
|
|
121
127
|
- `linux.grouping` - Linux only: replace notifications in-place instead of stacking (default: false). Requires `notify-send` 0.8+
|
|
122
128
|
|
|
@@ -131,11 +137,14 @@ Control each event separately:
|
|
|
131
137
|
"complete": { "sound": true, "notification": true },
|
|
132
138
|
"subagent_complete": { "sound": false, "notification": false },
|
|
133
139
|
"error": { "sound": true, "notification": true },
|
|
134
|
-
"question": { "sound": true, "notification": true }
|
|
140
|
+
"question": { "sound": true, "notification": true },
|
|
141
|
+
"user_cancelled": { "sound": false, "notification": false }
|
|
135
142
|
}
|
|
136
143
|
}
|
|
137
144
|
```
|
|
138
145
|
|
|
146
|
+
`user_cancelled` fires when you press ESC to abort a session. It's silent by default so intentional cancellations don't trigger error alerts. Set `sound` or `notification` to `true` if you want confirmation when cancelling.
|
|
147
|
+
|
|
139
148
|
Or use true/false for both:
|
|
140
149
|
|
|
141
150
|
```json
|
|
@@ -157,7 +166,8 @@ Customize the notification text:
|
|
|
157
166
|
"complete": "Session has finished: {sessionTitle}",
|
|
158
167
|
"subagent_complete": "Subagent task completed: {sessionTitle}",
|
|
159
168
|
"error": "Session encountered an error: {sessionTitle}",
|
|
160
|
-
"question": "Session has a question: {sessionTitle}"
|
|
169
|
+
"question": "Session has a question: {sessionTitle}",
|
|
170
|
+
"user_cancelled": "Session was cancelled by user: {sessionTitle}"
|
|
161
171
|
}
|
|
162
172
|
}
|
|
163
173
|
```
|
|
@@ -182,7 +192,8 @@ Use your own sound files:
|
|
|
182
192
|
"complete": "/path/to/done.wav",
|
|
183
193
|
"subagent_complete": "/path/to/subagent-done.wav",
|
|
184
194
|
"error": "/path/to/error.wav",
|
|
185
|
-
"question": "/path/to/question.wav"
|
|
195
|
+
"question": "/path/to/question.wav",
|
|
196
|
+
"user_cancelled": "/path/to/cancelled.wav"
|
|
186
197
|
}
|
|
187
198
|
}
|
|
188
199
|
```
|
|
@@ -203,7 +214,8 @@ Set per-event volume from `0` to `1`:
|
|
|
203
214
|
"complete": 0.3,
|
|
204
215
|
"subagent_complete": 0.15,
|
|
205
216
|
"error": 1,
|
|
206
|
-
"question": 0.7
|
|
217
|
+
"question": 0.7,
|
|
218
|
+
"user_cancelled": 0.5
|
|
207
219
|
}
|
|
208
220
|
}
|
|
209
221
|
```
|
|
@@ -267,6 +279,32 @@ Run your own script when something happens. Use `{event}`, `{message}`, and `{se
|
|
|
267
279
|
|
|
268
280
|
**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.
|
|
269
281
|
|
|
282
|
+
## Focus detection
|
|
283
|
+
|
|
284
|
+
When `suppressWhenFocused` is `true` (the default), notifications and sounds are skipped if the terminal running OpenCode is the active/focused window. The idea is simple: if you're already looking at it, you don't need an alert.
|
|
285
|
+
|
|
286
|
+
To disable this and always get notified:
|
|
287
|
+
|
|
288
|
+
```json
|
|
289
|
+
{
|
|
290
|
+
"suppressWhenFocused": false
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Platform support
|
|
295
|
+
|
|
296
|
+
| Platform | Method | Requirements |
|
|
297
|
+
|----------|--------|--------------|
|
|
298
|
+
| macOS | AppleScript (`System Events`) | None |
|
|
299
|
+
| Linux X11 | `xdotool` | `xdotool` installed |
|
|
300
|
+
| Linux Wayland (Hyprland) | `hyprctl activewindow` | None |
|
|
301
|
+
| Linux Wayland (Sway) | `swaymsg -t get_tree` | None |
|
|
302
|
+
| Linux Wayland (KDE) | `kdotool` | `kdotool` installed |
|
|
303
|
+
| Linux Wayland (GNOME) | Not supported | Falls back to always notifying |
|
|
304
|
+
| Windows | `GetForegroundWindow()` via PowerShell | None |
|
|
305
|
+
|
|
306
|
+
If detection fails for any reason (missing tools, unknown compositor, permissions), it falls back to always notifying. It never silently eats your notifications.
|
|
307
|
+
|
|
270
308
|
## Linux: Notification Grouping
|
|
271
309
|
|
|
272
310
|
By default, each notification appears as a separate entry. During active sessions this can create noise when multiple events fire quickly (e.g. permission + complete + question).
|
package/dist/index.js
CHANGED
|
@@ -3745,6 +3745,7 @@ var DEFAULT_CONFIG = {
|
|
|
3745
3745
|
showProjectName: true,
|
|
3746
3746
|
showSessionTitle: false,
|
|
3747
3747
|
showIcon: true,
|
|
3748
|
+
suppressWhenFocused: true,
|
|
3748
3749
|
notificationSystem: "osascript",
|
|
3749
3750
|
linux: {
|
|
3750
3751
|
grouping: false
|
|
@@ -3848,6 +3849,7 @@ function loadConfig() {
|
|
|
3848
3849
|
showProjectName: userConfig.showProjectName ?? DEFAULT_CONFIG.showProjectName,
|
|
3849
3850
|
showSessionTitle: userConfig.showSessionTitle ?? DEFAULT_CONFIG.showSessionTitle,
|
|
3850
3851
|
showIcon: userConfig.showIcon ?? DEFAULT_CONFIG.showIcon,
|
|
3852
|
+
suppressWhenFocused: userConfig.suppressWhenFocused ?? DEFAULT_CONFIG.suppressWhenFocused,
|
|
3851
3853
|
notificationSystem: userConfig.notificationSystem === "node-notifier" ? "node-notifier" : "osascript",
|
|
3852
3854
|
linux: {
|
|
3853
3855
|
grouping: typeof userConfig.linux?.grouping === "boolean" ? userConfig.linux.grouping : DEFAULT_CONFIG.linux.grouping
|
|
@@ -4198,6 +4200,228 @@ function runCommand2(config, event, message, sessionTitle, projectName) {
|
|
|
4198
4200
|
proc.unref();
|
|
4199
4201
|
}
|
|
4200
4202
|
|
|
4203
|
+
// src/focus.ts
|
|
4204
|
+
import { execSync } from "child_process";
|
|
4205
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
4206
|
+
var TERMINAL_MAP = {
|
|
4207
|
+
ghostty: { name: "Ghostty", macProcessNames: ["Ghostty", "ghostty"] },
|
|
4208
|
+
kitty: { name: "kitty", macProcessNames: ["kitty"] },
|
|
4209
|
+
alacritty: { name: "Alacritty", macProcessNames: ["Alacritty", "alacritty"] },
|
|
4210
|
+
wezterm: { name: "WezTerm", macProcessNames: ["WezTerm", "wezterm-gui"] },
|
|
4211
|
+
apple_terminal: { name: "Terminal", macProcessNames: ["Terminal"] },
|
|
4212
|
+
iterm: { name: "iTerm2", macProcessNames: ["iTerm2", "iTerm"] },
|
|
4213
|
+
warp: { name: "Warp", macProcessNames: ["Warp"] },
|
|
4214
|
+
vscode: { name: "VS Code", macProcessNames: ["Code", "Code - Insiders", "Cursor", "cursor"] },
|
|
4215
|
+
windows_terminal: { name: "Windows Terminal", macProcessNames: [] },
|
|
4216
|
+
tmux: { name: "tmux", macProcessNames: [] }
|
|
4217
|
+
};
|
|
4218
|
+
var cachedTerminal = undefined;
|
|
4219
|
+
function detectTerminal() {
|
|
4220
|
+
if (cachedTerminal !== undefined)
|
|
4221
|
+
return cachedTerminal;
|
|
4222
|
+
const env = process.env;
|
|
4223
|
+
let result = null;
|
|
4224
|
+
if (env.GHOSTTY_RESOURCES_DIR) {
|
|
4225
|
+
result = TERMINAL_MAP.ghostty;
|
|
4226
|
+
} else if (env.KITTY_PID) {
|
|
4227
|
+
result = TERMINAL_MAP.kitty;
|
|
4228
|
+
} else if (env.ALACRITTY_SOCKET || env.ALACRITTY_LOG) {
|
|
4229
|
+
result = TERMINAL_MAP.alacritty;
|
|
4230
|
+
} else if (env.WEZTERM_EXECUTABLE) {
|
|
4231
|
+
result = TERMINAL_MAP.wezterm;
|
|
4232
|
+
} else if (env.WT_SESSION) {
|
|
4233
|
+
result = TERMINAL_MAP.windows_terminal;
|
|
4234
|
+
} else if (env.VSCODE_PID || env.TERM_PROGRAM === "vscode") {
|
|
4235
|
+
result = TERMINAL_MAP.vscode;
|
|
4236
|
+
} else if (env.TERM_PROGRAM === "Apple_Terminal") {
|
|
4237
|
+
result = TERMINAL_MAP.apple_terminal;
|
|
4238
|
+
} else if (env.TERM_PROGRAM === "iTerm.app") {
|
|
4239
|
+
result = TERMINAL_MAP.iterm;
|
|
4240
|
+
} else if (env.TERM_PROGRAM === "WarpTerminal") {
|
|
4241
|
+
result = TERMINAL_MAP.warp;
|
|
4242
|
+
} else if (env.TMUX || env.TERM_PROGRAM === "tmux") {
|
|
4243
|
+
result = TERMINAL_MAP.tmux;
|
|
4244
|
+
}
|
|
4245
|
+
cachedTerminal = result;
|
|
4246
|
+
return result;
|
|
4247
|
+
}
|
|
4248
|
+
function execWithTimeout(command, timeoutMs = 500) {
|
|
4249
|
+
try {
|
|
4250
|
+
return execSync(command, { timeout: timeoutMs, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
4251
|
+
} catch {
|
|
4252
|
+
return null;
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
function isMacOSFocused(terminal) {
|
|
4256
|
+
const frontApp = execWithTimeout(`osascript -e 'tell application "System Events" to get name of first application process whose frontmost is true'`);
|
|
4257
|
+
if (!frontApp)
|
|
4258
|
+
return false;
|
|
4259
|
+
return terminal.macProcessNames.some((name) => name.toLowerCase() === frontApp.toLowerCase());
|
|
4260
|
+
}
|
|
4261
|
+
function isPidAncestorOfCurrentProcess(pid) {
|
|
4262
|
+
let currentPid = process.pid;
|
|
4263
|
+
for (let depth = 0;depth < 20; depth++) {
|
|
4264
|
+
if (currentPid === pid)
|
|
4265
|
+
return true;
|
|
4266
|
+
if (currentPid <= 1)
|
|
4267
|
+
return false;
|
|
4268
|
+
try {
|
|
4269
|
+
const stat = readFileSync2(`/proc/${currentPid}/stat`, "utf-8");
|
|
4270
|
+
const match = stat.match(/^\d+\s+\(.*?\)\s+\S+\s+(\d+)/);
|
|
4271
|
+
if (!match)
|
|
4272
|
+
return false;
|
|
4273
|
+
currentPid = parseInt(match[1], 10);
|
|
4274
|
+
} catch {
|
|
4275
|
+
return false;
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4278
|
+
return false;
|
|
4279
|
+
}
|
|
4280
|
+
function isLinuxX11Focused() {
|
|
4281
|
+
const pidStr = execWithTimeout("xdotool getactivewindow getwindowpid");
|
|
4282
|
+
if (!pidStr)
|
|
4283
|
+
return false;
|
|
4284
|
+
const pid = parseInt(pidStr, 10);
|
|
4285
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
4286
|
+
return false;
|
|
4287
|
+
return isPidAncestorOfCurrentProcess(pid);
|
|
4288
|
+
}
|
|
4289
|
+
function isHyprlandFocused() {
|
|
4290
|
+
const output = execWithTimeout("hyprctl activewindow -j");
|
|
4291
|
+
if (!output)
|
|
4292
|
+
return false;
|
|
4293
|
+
try {
|
|
4294
|
+
const data = JSON.parse(output);
|
|
4295
|
+
const pid = data?.pid;
|
|
4296
|
+
if (typeof pid !== "number" || pid <= 0)
|
|
4297
|
+
return false;
|
|
4298
|
+
return isPidAncestorOfCurrentProcess(pid);
|
|
4299
|
+
} catch {
|
|
4300
|
+
return false;
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
function isSwayFocused() {
|
|
4304
|
+
const output = execWithTimeout("swaymsg -t get_tree", 1000);
|
|
4305
|
+
if (!output)
|
|
4306
|
+
return false;
|
|
4307
|
+
try {
|
|
4308
|
+
const tree = JSON.parse(output);
|
|
4309
|
+
const pid = findFocusedPid(tree);
|
|
4310
|
+
if (pid === null)
|
|
4311
|
+
return false;
|
|
4312
|
+
return isPidAncestorOfCurrentProcess(pid);
|
|
4313
|
+
} catch {
|
|
4314
|
+
return false;
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
function findFocusedPid(node) {
|
|
4318
|
+
if (node.focused === true && typeof node.pid === "number") {
|
|
4319
|
+
return node.pid;
|
|
4320
|
+
}
|
|
4321
|
+
if (Array.isArray(node.nodes)) {
|
|
4322
|
+
for (const child of node.nodes) {
|
|
4323
|
+
const pid = findFocusedPid(child);
|
|
4324
|
+
if (pid !== null)
|
|
4325
|
+
return pid;
|
|
4326
|
+
}
|
|
4327
|
+
}
|
|
4328
|
+
if (Array.isArray(node.floating_nodes)) {
|
|
4329
|
+
for (const child of node.floating_nodes) {
|
|
4330
|
+
const pid = findFocusedPid(child);
|
|
4331
|
+
if (pid !== null)
|
|
4332
|
+
return pid;
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
return null;
|
|
4336
|
+
}
|
|
4337
|
+
function isKDEWaylandFocused() {
|
|
4338
|
+
const windowId = execWithTimeout("kdotool getactivewindow");
|
|
4339
|
+
if (!windowId)
|
|
4340
|
+
return false;
|
|
4341
|
+
const pidStr = execWithTimeout(`kdotool getwindowpid ${windowId}`);
|
|
4342
|
+
if (!pidStr)
|
|
4343
|
+
return false;
|
|
4344
|
+
const pid = parseInt(pidStr, 10);
|
|
4345
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
4346
|
+
return false;
|
|
4347
|
+
return isPidAncestorOfCurrentProcess(pid);
|
|
4348
|
+
}
|
|
4349
|
+
function isLinuxWaylandFocused() {
|
|
4350
|
+
const env = process.env;
|
|
4351
|
+
if (env.HYPRLAND_INSTANCE_SIGNATURE) {
|
|
4352
|
+
return isHyprlandFocused();
|
|
4353
|
+
}
|
|
4354
|
+
if (env.SWAYSOCK) {
|
|
4355
|
+
return isSwayFocused();
|
|
4356
|
+
}
|
|
4357
|
+
if (env.KDE_SESSION_VERSION) {
|
|
4358
|
+
return isKDEWaylandFocused();
|
|
4359
|
+
}
|
|
4360
|
+
return false;
|
|
4361
|
+
}
|
|
4362
|
+
function isWindowsFocused() {
|
|
4363
|
+
const script = `
|
|
4364
|
+
Add-Type @"
|
|
4365
|
+
using System;
|
|
4366
|
+
using System.Runtime.InteropServices;
|
|
4367
|
+
public class FocusHelper {
|
|
4368
|
+
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
|
4369
|
+
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint pid);
|
|
4370
|
+
}
|
|
4371
|
+
"@
|
|
4372
|
+
$hwnd = [FocusHelper]::GetForegroundWindow()
|
|
4373
|
+
$pid = 0
|
|
4374
|
+
[void][FocusHelper]::GetWindowThreadProcessId($hwnd, [ref]$pid)
|
|
4375
|
+
Write-Output $pid
|
|
4376
|
+
`.trim().replace(/\n/g, "; ");
|
|
4377
|
+
const pidStr = execWithTimeout(`powershell -NoProfile -Command "${script}"`, 1000);
|
|
4378
|
+
if (!pidStr)
|
|
4379
|
+
return false;
|
|
4380
|
+
const pid = parseInt(pidStr, 10);
|
|
4381
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
4382
|
+
return false;
|
|
4383
|
+
let currentPid = process.pid;
|
|
4384
|
+
for (let depth = 0;depth < 20; depth++) {
|
|
4385
|
+
if (currentPid === pid)
|
|
4386
|
+
return true;
|
|
4387
|
+
if (currentPid <= 1)
|
|
4388
|
+
return false;
|
|
4389
|
+
const parentPidStr = execWithTimeout(`powershell -NoProfile -Command "(Get-Process -Id ${currentPid}).Parent.Id"`, 500);
|
|
4390
|
+
if (!parentPidStr)
|
|
4391
|
+
return false;
|
|
4392
|
+
currentPid = parseInt(parentPidStr, 10);
|
|
4393
|
+
if (!Number.isFinite(currentPid))
|
|
4394
|
+
return false;
|
|
4395
|
+
}
|
|
4396
|
+
return false;
|
|
4397
|
+
}
|
|
4398
|
+
function isTerminalFocused() {
|
|
4399
|
+
try {
|
|
4400
|
+
const platform3 = process.platform;
|
|
4401
|
+
if (platform3 === "darwin") {
|
|
4402
|
+
const terminal = detectTerminal();
|
|
4403
|
+
if (!terminal || terminal.macProcessNames.length === 0)
|
|
4404
|
+
return false;
|
|
4405
|
+
return isMacOSFocused(terminal);
|
|
4406
|
+
}
|
|
4407
|
+
if (platform3 === "linux") {
|
|
4408
|
+
if (process.env.WAYLAND_DISPLAY) {
|
|
4409
|
+
return isLinuxWaylandFocused();
|
|
4410
|
+
}
|
|
4411
|
+
if (process.env.DISPLAY) {
|
|
4412
|
+
return isLinuxX11Focused();
|
|
4413
|
+
}
|
|
4414
|
+
return false;
|
|
4415
|
+
}
|
|
4416
|
+
if (platform3 === "win32") {
|
|
4417
|
+
return isWindowsFocused();
|
|
4418
|
+
}
|
|
4419
|
+
return false;
|
|
4420
|
+
} catch {
|
|
4421
|
+
return false;
|
|
4422
|
+
}
|
|
4423
|
+
}
|
|
4424
|
+
|
|
4201
4425
|
// src/index.ts
|
|
4202
4426
|
var IDLE_COMPLETE_DELAY_MS = 350;
|
|
4203
4427
|
var pendingIdleTimers = new Map;
|
|
@@ -4229,6 +4453,9 @@ function getNotificationTitle(config, projectName) {
|
|
|
4229
4453
|
return "OpenCode";
|
|
4230
4454
|
}
|
|
4231
4455
|
async function handleEvent(config, eventType, projectName, elapsedSeconds, sessionTitle) {
|
|
4456
|
+
if (config.suppressWhenFocused && isTerminalFocused()) {
|
|
4457
|
+
return;
|
|
4458
|
+
}
|
|
4232
4459
|
const promises = [];
|
|
4233
4460
|
const rawMessage = getMessage(config, eventType);
|
|
4234
4461
|
const message = interpolateMessage(rawMessage, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mohak34/opencode-notifier",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30-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",
|