relay-companion 0.1.64 → 0.1.65

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/overlay/main.cjs CHANGED
@@ -42,6 +42,7 @@ const {
42
42
  shouldIgnoreDismiss,
43
43
  boundedPresentedRelayIds,
44
44
  recoverInterruptedAttentionPrefs,
45
+ quitRelayCommand,
45
46
  hostPollDelayMs,
46
47
  sentRefreshDelayMs,
47
48
  } = require("./visibility.cjs");
@@ -2175,6 +2176,24 @@ function syncTray() {
2175
2176
  } catch {}
2176
2177
  }
2177
2178
 
2179
+ // "Quit Relay" from the tray: stop the daemon and the pill's own service via a
2180
+ // DETACHED shell (the second bootout kills this process, so the shell must
2181
+ // outlive us), then quit the app immediately for instant visual feedback.
2182
+ function quitRelayCompletely() {
2183
+ try {
2184
+ const uid = typeof process.getuid === "function" ? process.getuid() : 501;
2185
+ const [cmd, args] = quitRelayCommand({ platform: process.platform, uid });
2186
+ const child = spawn(cmd, args, { detached: true, stdio: "ignore" });
2187
+ child.on("error", (error) => console.error(`[overlay] ${new Date().toISOString()} quit spawn failed:`, error && error.message));
2188
+ child.unref();
2189
+ } catch (error) {
2190
+ console.error(`[overlay] ${new Date().toISOString()} quit failed:`, error && error.message);
2191
+ }
2192
+ try {
2193
+ app.quit();
2194
+ } catch {}
2195
+ }
2196
+
2178
2197
  function createTray() {
2179
2198
  const icon = createTrayIcon();
2180
2199
  if (!icon || icon.isEmpty()) {
@@ -2195,7 +2214,11 @@ function createTray() {
2195
2214
  tray.on("right-click", () => {
2196
2215
  try {
2197
2216
  tray.popUpContextMenu(
2198
- Menu.buildFromTemplate([{ label: pillIsOnScreen() ? "Hide Relay" : "Show Relay", click: toggleFromTray }]),
2217
+ Menu.buildFromTemplate([
2218
+ { label: pillIsOnScreen() ? "Hide Relay" : "Show Relay", click: toggleFromTray },
2219
+ { type: "separator" },
2220
+ { label: "Quit Relay", click: quitRelayCompletely },
2221
+ ]),
2199
2222
  );
2200
2223
  } catch (error) {
2201
2224
  console.error("[overlay] tray menu failed:", error && error.message);
@@ -119,7 +119,30 @@ function sentRefreshDelayMs({ testMode = false, engaged = false, showActive = fa
119
119
  return engaged || showActive ? 5000 : 30000;
120
120
  }
121
121
 
122
+ // The tray's "Quit Relay": one detached shell that stops BOTH background
123
+ // services. Order matters — the daemon first, the pill's own service LAST,
124
+ // because the second bootout kills the process that spawned the shell.
125
+ // launchctl bootout (not kickstart/stop) is the verb that beats KeepAlive;
126
+ // the jobs return at next login (RunAtLoad) or via `relay pill`.
127
+ function quitRelayCommand({ platform = process.platform, uid = 501 } = {}) {
128
+ if (platform === "win32") {
129
+ return [
130
+ "cmd",
131
+ ["/c", 'schtasks /End /TN "Relay Companion Daemon" & schtasks /End /TN "Relay Companion Pill"'],
132
+ ];
133
+ }
134
+ return [
135
+ "/bin/sh",
136
+ [
137
+ "-c",
138
+ `/bin/launchctl bootout gui/${uid}/work.relay.companion 2>/dev/null; ` +
139
+ `/bin/launchctl bootout gui/${uid}/work.relay.companion.pill 2>/dev/null`,
140
+ ],
141
+ ];
142
+ }
143
+
122
144
  module.exports = {
145
+ quitRelayCommand,
123
146
  hostPollDelayMs,
124
147
  sentRefreshDelayMs,
125
148
  overlayWanted,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relay-companion",
3
- "version": "0.1.64",
3
+ "version": "0.1.65",
4
4
  "description": "Relay companion for ordinary messages, with dormant coordination features available only by explicit opt-in.",
5
5
  "type": "module",
6
6
  "bin": {