thinkpool-pair 0.7.274 → 0.7.275

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/account.mjs +6 -8
  2. package/package.json +1 -1
  3. package/service.mjs +12 -15
package/account.mjs CHANGED
@@ -501,12 +501,11 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
501
501
  acct.on('broadcast', { event: 'restart' }, () => {
502
502
  restarting = true
503
503
  // The dashboard "Restart bridge" button ALSO updates to the newest published version
504
- // (Max, 2026-07-03). If an OS service manages this bridge, spawn a DETACHED updater
505
- // that re-pins the service to @latest and reloads it (install-service resolves the
506
- // newest version → rewrites the plist/unit → bootout+bootstrap). Detached → its own
507
- // session, so it SURVIVES this process being booted out (the reload kills us); it
508
- // inherits our env, whose PATH carries nvm's bin, so npx/node resolve — a bare launchd
509
- // PATH would NOT (the 2026-07-03 gotcha that made a manual updater silently no-op).
504
+ // (Max, 2026-07-03). Use service.mjs's authoritative registry → immutable runtime →
505
+ // one-shot reload transaction directly. Spawning `npx install-service` here used a
506
+ // disposable cache that legacy services could delete underneath the updater. The
507
+ // service primitive arms an independent launchd handoff before this process is booted
508
+ // out, so the reload survives us without a second package execution tree.
510
509
  // The reload IS the bounce; sessions resume from disk on the new version. Best-effort:
511
510
  // if there's no service or npm is unreachable, we fall through to the plain bounce, so
512
511
  // the button is never dead — and even a failed update degrades to today's behaviour.
@@ -525,8 +524,7 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
525
524
  try {
526
525
  const svc = await import('./service.mjs')
527
526
  if (svc.serviceActive(null)) {
528
- spawn('npx', ['-y', 'thinkpool-pair@latest', 'install-service'], { detached: true, stdio: 'ignore', env: process.env }).unref()
529
- updating = true
527
+ updating = svc.updateService(null) !== false
530
528
  }
531
529
  } catch { /* no service / spawn failed → plain bounce below */ }
532
530
  process.stderr.write(updating
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.274",
3
+ "version": "0.7.275",
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
@@ -46,16 +46,12 @@ export function darwinServiceRunning(output) {
46
46
  const text = String(output || '')
47
47
  return /(^|\n)[ \t]*state = running[ \t]*($|\n)/.test(text) && /(^|\n)[ \t]*pid = \d+[ \t]*($|\n)/.test(text)
48
48
  }
49
- // Legacy auto-update services still use npx. Wipe its disposable execution project
50
- // before resolving the package so an @latest restart cannot re-serve a stale cache.
51
- // `home` is the platform-correct, runtime-expanded home token ("$HOME" on darwin under
52
- // /bin/bash; "$$HOME" inside a systemd unit so systemd emits a literal $HOME to bash).
53
- // npx caches the temporary execution project as a unit. Removing only the package can
54
- // leave its generated .bin/package-lock metadata behind, producing the contradictory
55
- // state where `npm view` sees a version but `npx` reports ETARGET or no executable.
56
- // The whole _npx directory is disposable; clear its children so the next launch is a
57
- // genuinely clean install.
58
- const cacheWipe = (home) => `rm -rf ${home}/.npm/_npx/* 2>/dev/null`
49
+ // Legacy auto-update services still use npx. They MUST use a bridge-owned cache:
50
+ // deleting ~/.npm/_npx corrupts unrelated interactive `npx thinkpool-pair` processes
51
+ // (0.7.274 removed account.mjs underneath a live launcher). The service cache is
52
+ // disposable and isolated, so clearing it cannot mutate a person's npm execution tree.
53
+ const serviceNpmCache = () => path.join(os.homedir(), '.thinkpool-pair', 'npm-cache')
54
+ const cacheWipe = (cacheRoot) => `rm -rf ${shq(path.join(cacheRoot, '_npx'))}/* 2>/dev/null`
59
55
 
60
56
  // The version installing the service. By DEFAULT the service is pinned to this exact
61
57
  // version (not @latest) so a future bad npm publish can't auto-roll to every machine's
@@ -130,6 +126,7 @@ export function buildArtifact(platform, { room = null, cmdArgs = [], autoUpdate
130
126
  const logDir = path.join(os.homedir(), '.thinkpool-pair')
131
127
  const log = path.join(logDir, `${id}.log`)
132
128
  const servicePath = sanitizeServicePath(process.env.PATH || '', platform)
129
+ const npmCache = serviceNpmCache()
133
130
  const tail = cmdArgs.length ? ['--', ...cmdArgs] : []
134
131
  const desc = room ? `bridge (${room})` : 'account bridge'
135
132
  // OS-supervised tiers (launchd KeepAlive / systemd Restart) don't need --supervise.
@@ -156,7 +153,7 @@ export function buildArtifact(platform, { room = null, cmdArgs = [], autoUpdate
156
153
  const args = runtimeEntry
157
154
  ? [process.execPath, runtimeEntry, ...(room ? [room] : []), ...tail]
158
155
  : staleProof
159
- ? ['/bin/bash', '-lc', `${cacheWipe('"$HOME"')}; exec ${[npx, ...pkgArgs, ...tail].map(shq).join(' ')}`]
156
+ ? ['/bin/bash', '-lc', `${cacheWipe(npmCache)}; export NPM_CONFIG_CACHE=${shq(npmCache)}; exec ${[npx, ...pkgArgs, ...tail].map(shq).join(' ')}`]
160
157
  : [npx, ...pkgArgs, ...tail]
161
158
  const file = path.join(os.homedir(), 'Library', 'LaunchAgents', `${label(room)}.plist`)
162
159
  const content = `<?xml version="1.0" encoding="UTF-8"?>
@@ -175,7 +172,7 @@ export function buildArtifact(platform, { room = null, cmdArgs = [], autoUpdate
175
172
  <key>WorkingDirectory</key><string>${xml(cwd)}</string>
176
173
  <key>StandardOutPath</key><string>${xml(log)}</string>
177
174
  <key>StandardErrorPath</key><string>${xml(log)}</string>
178
- <key>EnvironmentVariables</key><dict><key>PATH</key><string>${xml(servicePath)}</string>${autoUpdate ? '<key>THINKPOOL_PAIR_AUTOUPDATE</key><string>1</string>' : ''}</dict>
175
+ <key>EnvironmentVariables</key><dict><key>PATH</key><string>${xml(servicePath)}</string>${runtimeEntry ? '' : `<key>NPM_CONFIG_CACHE</key><string>${xml(npmCache)}</string>`}${autoUpdate ? '<key>THINKPOOL_PAIR_AUTOUPDATE</key><string>1</string>' : ''}</dict>
179
176
  </dict></plist>\n`
180
177
  // The destructive reload is intentionally NOT represented as an inline `post`
181
178
  // command. installService stages this plist and hands the transaction to an
@@ -193,7 +190,7 @@ export function buildArtifact(platform, { room = null, cmdArgs = [], autoUpdate
193
190
  const execStart = runtimeEntry
194
191
  ? args.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(' ')
195
192
  : staleProof
196
- ? `/bin/bash -lc "${cacheWipe('$$HOME')}; exec ${[npx, ...pkgArgs, ...tail].join(' ')}"`
193
+ ? `/bin/bash -lc "${cacheWipe(npmCache)}; export NPM_CONFIG_CACHE=${shq(npmCache)}; exec ${[npx, ...pkgArgs, ...tail].join(' ')}"`
197
194
  : args.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(' ')
198
195
  const content = `[Unit]
199
196
  Description=ThinkPool Code ${desc}
@@ -211,7 +208,7 @@ RestartSec=2
211
208
  RestartPreventExitStatus=0
212
209
  WorkingDirectory=${cwd}
213
210
  Environment=PATH=${servicePath}
214
- ${autoUpdate ? 'Environment=THINKPOOL_PAIR_AUTOUPDATE=1\n' : ''}StandardOutput=append:${log}
211
+ ${runtimeEntry ? '' : `Environment=NPM_CONFIG_CACHE=${npmCache}\n`}${autoUpdate ? 'Environment=THINKPOOL_PAIR_AUTOUPDATE=1\n' : ''}StandardOutput=append:${log}
215
212
  StandardError=append:${log}
216
213
 
217
214
  [Install]
@@ -232,7 +229,7 @@ WantedBy=default.target
232
229
  : room
233
230
  ? ['npx', '-y', ...onlineFlag, verSpec, room, '--supervise', ...tail].join(' ')
234
231
  : ['npx', '-y', ...onlineFlag, verSpec].join(' ')
235
- const content = `@echo off\r\ntitle thinkpool-pair ${id}\r\n${inner}\r\n`
232
+ const content = `@echo off\r\ntitle thinkpool-pair ${id}\r\n${runtimeEntry ? '' : `set "NPM_CONFIG_CACHE=${npmCache}"\r\n`}${inner}\r\n`
236
233
  return { file, content, logDir, post: [], note: 'Installed to the Startup folder — runs at login' + (room ? ' with --supervise (auto-restart on crash).' : ' (account mode).') + ' Start it now without rebooting by double-clicking the .cmd, or run it from a terminal.' }
237
234
  }
238
235