@mohak34/opencode-notifier 0.1.27-beta.1 → 0.1.27

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 +22 -0
  2. package/dist/index.js +65 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -55,6 +55,9 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
55
55
  "showSessionTitle": false,
56
56
  "showIcon": true,
57
57
  "notificationSystem": "osascript",
58
+ "linux": {
59
+ "grouping": false
60
+ },
58
61
  "command": {
59
62
  "enabled": false,
60
63
  "path": "/path/to/command",
@@ -115,6 +118,7 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
115
118
  - `showSessionTitle` - Include the session title in notification messages via `{sessionTitle}` placeholder (default: true)
116
119
  - `showIcon` - Show OpenCode icon, Windows/Linux only (default: true)
117
120
  - `notificationSystem` - macOS only: `"osascript"` or `"node-notifier"` (default: "osascript")
121
+ - `linux.grouping` - Linux only: replace notifications in-place instead of stacking (default: false). Requires `notify-send` 0.8+
118
122
 
119
123
  ### Events
120
124
 
@@ -263,6 +267,24 @@ Run your own script when something happens. Use `{event}`, `{message}`, and `{se
263
267
 
264
268
  **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.
265
269
 
270
+ ## Linux: Notification Grouping
271
+
272
+ 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).
273
+
274
+ Enable grouping to replace notifications in-place instead of stacking:
275
+
276
+ ```json
277
+ {
278
+ "linux": {
279
+ "grouping": true
280
+ }
281
+ }
282
+ ```
283
+
284
+ With grouping enabled, each new notification replaces the previous one so you only see the latest event. This requires `notify-send` 0.8+ (standard on Ubuntu 22.04+, Debian 12+, Fedora 36+, Arch). On older systems it falls back to the default stacking behavior automatically.
285
+
286
+ Works with all major notification daemons (GNOME, dunst, mako, swaync, etc.) on both X11 and Wayland.
287
+
266
288
  ## Updating
267
289
 
268
290
  If Opencode does not update the plugin or there is an issue with the cache version:
package/dist/index.js CHANGED
@@ -3746,6 +3746,9 @@ var DEFAULT_CONFIG = {
3746
3746
  showSessionTitle: false,
3747
3747
  showIcon: true,
3748
3748
  notificationSystem: "osascript",
3749
+ linux: {
3750
+ grouping: false
3751
+ },
3749
3752
  command: {
3750
3753
  enabled: false,
3751
3754
  path: "",
@@ -3842,6 +3845,9 @@ function loadConfig() {
3842
3845
  showSessionTitle: userConfig.showSessionTitle ?? DEFAULT_CONFIG.showSessionTitle,
3843
3846
  showIcon: userConfig.showIcon ?? DEFAULT_CONFIG.showIcon,
3844
3847
  notificationSystem: userConfig.notificationSystem === "node-notifier" ? "node-notifier" : "osascript",
3848
+ linux: {
3849
+ grouping: typeof userConfig.linux?.grouping === "boolean" ? userConfig.linux.grouping : DEFAULT_CONFIG.linux.grouping
3850
+ },
3845
3851
  command: {
3846
3852
  enabled: typeof userCommand.enabled === "boolean" ? userCommand.enabled : DEFAULT_CONFIG.command.enabled,
3847
3853
  path: typeof userCommand.path === "string" ? userCommand.path : DEFAULT_CONFIG.command.path,
@@ -3928,7 +3934,7 @@ function interpolateMessage(message, context) {
3928
3934
  // src/notify.ts
3929
3935
  var import_node_notifier = __toESM(require_node_notifier(), 1);
3930
3936
  import os from "os";
3931
- import { exec } from "child_process";
3937
+ import { exec, execFile } from "child_process";
3932
3938
  var DEBOUNCE_MS = 1000;
3933
3939
  var platform = os.type();
3934
3940
  var platformNotifier;
@@ -3942,7 +3948,53 @@ if (platform === "Linux" || platform.match(/BSD$/)) {
3942
3948
  platformNotifier = import_node_notifier.default;
3943
3949
  }
3944
3950
  var lastNotificationTime = {};
3945
- async function sendNotification(title, message, timeout, iconPath, notificationSystem = "osascript") {
3951
+ var lastLinuxNotificationId = null;
3952
+ var linuxNotifySendSupportsReplace = null;
3953
+ function detectNotifySendCapabilities() {
3954
+ return new Promise((resolve) => {
3955
+ execFile("notify-send", ["--version"], (error, stdout) => {
3956
+ if (error) {
3957
+ resolve(false);
3958
+ return;
3959
+ }
3960
+ const match = stdout.match(/(\d+)\.(\d+)/);
3961
+ if (match) {
3962
+ const major = parseInt(match[1], 10);
3963
+ const minor = parseInt(match[2], 10);
3964
+ resolve(major > 0 || major === 0 && minor >= 8);
3965
+ return;
3966
+ }
3967
+ resolve(false);
3968
+ });
3969
+ });
3970
+ }
3971
+ function sendLinuxNotificationDirect(title, message, timeout, iconPath, grouping = true) {
3972
+ return new Promise((resolve) => {
3973
+ const args = [];
3974
+ if (iconPath) {
3975
+ args.push("--icon", iconPath);
3976
+ }
3977
+ args.push("--expire-time", String(timeout * 1000));
3978
+ args.push("--app-name", "OpenCode");
3979
+ if (grouping && lastLinuxNotificationId !== null) {
3980
+ args.push("--replace-id", String(lastLinuxNotificationId));
3981
+ }
3982
+ if (grouping) {
3983
+ args.push("--print-id");
3984
+ }
3985
+ args.push("--", title, message);
3986
+ execFile("notify-send", args, (error, stdout) => {
3987
+ if (!error && grouping && stdout) {
3988
+ const id = parseInt(stdout.trim(), 10);
3989
+ if (!isNaN(id)) {
3990
+ lastLinuxNotificationId = id;
3991
+ }
3992
+ }
3993
+ resolve();
3994
+ });
3995
+ });
3996
+ }
3997
+ async function sendNotification(title, message, timeout, iconPath, notificationSystem = "osascript", linuxGrouping = true) {
3946
3998
  const now = Date.now();
3947
3999
  if (lastNotificationTime[message] && now - lastNotificationTime[message] < DEBOUNCE_MS) {
3948
4000
  return;
@@ -3970,6 +4022,16 @@ async function sendNotification(title, message, timeout, iconPath, notificationS
3970
4022
  });
3971
4023
  });
3972
4024
  }
4025
+ if (platform === "Linux" || platform.match(/BSD$/)) {
4026
+ if (linuxGrouping) {
4027
+ if (linuxNotifySendSupportsReplace === null) {
4028
+ linuxNotifySendSupportsReplace = await detectNotifySendCapabilities();
4029
+ }
4030
+ if (linuxNotifySendSupportsReplace) {
4031
+ return sendLinuxNotificationDirect(title, message, timeout, iconPath, true);
4032
+ }
4033
+ }
4034
+ }
3973
4035
  return new Promise((resolve) => {
3974
4036
  const notificationOptions = {
3975
4037
  title,
@@ -4169,7 +4231,7 @@ async function handleEvent(config, eventType, projectName, elapsedSeconds, sessi
4169
4231
  if (isEventNotificationEnabled(config, eventType)) {
4170
4232
  const title = getNotificationTitle(config, projectName);
4171
4233
  const iconPath = getIconPath(config);
4172
- promises.push(sendNotification(title, message, config.timeout, iconPath, config.notificationSystem));
4234
+ promises.push(sendNotification(title, message, config.timeout, iconPath, config.notificationSystem, config.linux.grouping));
4173
4235
  }
4174
4236
  if (isEventSoundEnabled(config, eventType)) {
4175
4237
  const customSoundPath = getSoundPath(config, eventType);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohak34/opencode-notifier",
3
- "version": "0.1.27-beta.1",
3
+ "version": "0.1.27",
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",