thinkpool-pair 0.7.102 → 0.7.103

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 (2) hide show
  1. package/package.json +1 -1
  2. package/service.mjs +18 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.102",
3
+ "version": "0.7.103",
4
4
  "description": "Share a local coding-agent CLI (Claude Code, Codex, Gemini, Aider, …) into a ThinkPool Code room, live.",
5
5
  "type": "module",
6
6
  "bin": {
package/service.mjs CHANGED
@@ -115,12 +115,25 @@ export function buildArtifact(platform, { room = null, cmdArgs = [], autoUpdate
115
115
  // retried on the EIO race → a final `print` so the command's exit code reports the
116
116
  // real loaded state (installService warns if it ends not-loaded).
117
117
  const lbl = label(room), dom = 'gui/$(id -u)'
118
+ const pr = `launchctl print ${dom}/${lbl} >/dev/null 2>&1`
118
119
  const post = [
119
- `launchctl bootout ${dom}/${lbl} 2>/dev/null`,
120
- `for i in $(seq 1 20); do launchctl print ${dom}/${lbl} >/dev/null 2>&1 || break; sleep 0.2; done`,
121
- `launchctl enable ${dom}/${lbl}`,
122
- `for i in $(seq 1 20); do launchctl bootstrap ${dom} "${file}" 2>/dev/null && break; sleep 0.3; done`,
123
- `launchctl print ${dom}/${lbl} >/dev/null 2>&1`,
120
+ // Evict any existing instance so the REWRITTEN plist (new pinned version) is what
121
+ // loads. bootout is async + occasionally sticky: if the old supervised process
122
+ // won't leave the domain, the follow-up bootstrap throws "Bootstrap failed: 5:
123
+ // Input/output error" and we'd SILENTLY keep running the OLD pinned version while
124
+ // reporting success — the 2026-06-30 "Restart & update" stuck-on-0.7.96 bug. So:
125
+ // bootout, wait for it to actually leave, and if it WON'T, SIGKILL the wedged pid
126
+ // then bootout again (KeepAlive can't relaunch a job that's booting out).
127
+ `launchctl bootout ${dom}/${lbl} 2>/dev/null || true`,
128
+ `for i in $(seq 1 25); do ${pr} || break; sleep 0.2; done`,
129
+ `if ${pr}; then p=$(launchctl print ${dom}/${lbl} 2>/dev/null | sed -n 's/.*[^a-z]pid = \\([0-9][0-9]*\\).*/\\1/p' | head -1); [ -n "$p" ] && kill -9 "$p" 2>/dev/null || true; launchctl bootout ${dom}/${lbl} 2>/dev/null || true; for i in $(seq 1 25); do ${pr} || break; sleep 0.2; done; fi`,
130
+ // Clear a stale `disabled` override (from a prior bootout / account-storm cleanup),
131
+ // then load the rewritten plist from disk, retried on the EIO teardown race.
132
+ `launchctl enable ${dom}/${lbl} 2>/dev/null || true`,
133
+ `ok=0; for i in $(seq 1 25); do launchctl bootstrap ${dom} "${file}" 2>/dev/null && { ok=1; break; }; sleep 0.3; done`,
134
+ // Be HONEST about the outcome: if it never loaded, say so + give the exact manual
135
+ // recovery — never let a failed reload masquerade as a successful update.
136
+ `if [ "$ok" != 1 ] && ! ${pr}; then echo " ⚠ thinkpool-pair: launchd reload FAILED — run by hand: launchctl bootout ${dom}/${lbl} ; launchctl bootstrap ${dom} \\"${file}\\"" >&2; fi`,
124
137
  ].join('; ')
125
138
  return { file, content, logDir, post: [post], note: 'Loaded as a LaunchAgent — starts at login, restarts on crash.' }
126
139
  }