unsnooze 1.12.1 → 1.12.2
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/CHANGELOG.md +11 -0
- package/bin/unsnooze.js +15 -0
- package/package.json +1 -1
- package/src/install.js +22 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.12.2 — 2026-07-16
|
|
6
|
+
|
|
7
|
+
- **Daemon autostart self-heal**: updating via npm never touches the
|
|
8
|
+
launchd/systemd unit file, so pre-1.12 users would keep the PATH-less unit
|
|
9
|
+
(daemon can't find tmux → every revival dies) until they manually re-ran
|
|
10
|
+
`unsnooze install --daemon`. The daemon now detects a PATH-less unit on
|
|
11
|
+
startup, regenerates it, and reloads itself — every affected user is fixed
|
|
12
|
+
automatically on their first daemon restart after updating (at the latest,
|
|
13
|
+
the next reboot). Manual `unsnooze install --daemon` still works and is no
|
|
14
|
+
longer required.
|
|
15
|
+
|
|
5
16
|
## 1.12.1 — 2026-07-16
|
|
6
17
|
|
|
7
18
|
- CI-only: pin the platform in a doctor test whose `launchctl` assertion
|
package/bin/unsnooze.js
CHANGED
|
@@ -199,6 +199,21 @@ async function main() {
|
|
|
199
199
|
const controller = new AbortController();
|
|
200
200
|
process.on('SIGTERM', () => controller.abort());
|
|
201
201
|
process.on('SIGINT', () => controller.abort());
|
|
202
|
+
// Self-heal pre-1.12 autostart units: they lack PATH, so this daemon
|
|
203
|
+
// cannot find tmux and every revival dies. Healing rewrites the unit
|
|
204
|
+
// and reloads it — which intentionally kills THIS process; the
|
|
205
|
+
// supervisor restarts us under the fixed unit. One-time: healed units
|
|
206
|
+
// pass the check forever after.
|
|
207
|
+
const installMod = await safeImport('../src/install.js');
|
|
208
|
+
if (installMod?.healDaemonAutostart) {
|
|
209
|
+
try {
|
|
210
|
+
const healed = installMod.healDaemonAutostart();
|
|
211
|
+
if (healed) {
|
|
212
|
+
const lm = await safeImport('../src/logger.js');
|
|
213
|
+
lm?.log('daemon', `autostart unit lacked PATH — regenerated ${healed}, reloading (self-heal)`);
|
|
214
|
+
}
|
|
215
|
+
} catch { /* heal is best-effort — a broken unit must not block the daemon */ }
|
|
216
|
+
}
|
|
202
217
|
// daemon.log is launchd/systemd-captured stdout+stderr. launchd holds
|
|
203
218
|
// an open fd on it for our whole lifetime, so rotation must be
|
|
204
219
|
// copy-truncate: renaming would leave launchd appending to the renamed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unsnooze",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.2",
|
|
4
4
|
"description": "Auto-resume Claude Code, Codex CLI, Grok, Qwen Code, Kimi CLI, OpenCode & Antigravity when the 5-hour/weekly usage limit resets — tmux, Zellij, VS Code & desktop apps. Wakes every limit-stopped AI coding session automatically.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
package/src/install.js
CHANGED
|
@@ -244,6 +244,28 @@ export function installDaemonAutostart({ platform = process.platform, dir = null
|
|
|
244
244
|
return null; // native Windows: no supported multiplexer to revive into
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
// One-time self-heal for units written before 1.12.0: they carry no PATH, so
|
|
248
|
+
// the daemon cannot find tmux (launchd gives daemons /usr/bin:/bin:… only) and
|
|
249
|
+
// every revival dies with spawn ENOENT. npm updates never touch the unit file,
|
|
250
|
+
// so the daemon repairs it on startup: regenerate + reload. Reloading kills
|
|
251
|
+
// the calling daemon — by design; the supervisor restarts it under the fixed
|
|
252
|
+
// unit. Returns the healed target, or null when nothing needed healing.
|
|
253
|
+
export function healDaemonAutostart({ platform = process.platform, dir = null, activate = defaultActivate } = {}) {
|
|
254
|
+
const marker = platform === 'darwin' ? 'EnvironmentVariables' : 'Environment="PATH=';
|
|
255
|
+
const target = platform === 'darwin'
|
|
256
|
+
? join(dir || autostartDir(platform), `${DAEMON_LABEL}.plist`)
|
|
257
|
+
: platform === 'linux'
|
|
258
|
+
? join(dir || autostartDir(platform), 'unsnooze.service')
|
|
259
|
+
: null;
|
|
260
|
+
if (!target || !existsSync(target)) return null; // not a daemon-autostart user
|
|
261
|
+
try {
|
|
262
|
+
if (readFileSync(target, 'utf-8').includes(marker)) return null; // already current
|
|
263
|
+
} catch {
|
|
264
|
+
return null; // unreadable — leave it alone
|
|
265
|
+
}
|
|
266
|
+
return installDaemonAutostart({ platform, dir, activate });
|
|
267
|
+
}
|
|
268
|
+
|
|
247
269
|
export function uninstallDaemonAutostart({ platform = process.platform, dir = null, activate = defaultActivate } = {}) {
|
|
248
270
|
if (platform === 'darwin') {
|
|
249
271
|
const target = join(dir || autostartDir(platform), `${DAEMON_LABEL}.plist`);
|