thinkpool-pair 0.7.192 → 0.7.193

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/bridge.mjs +6 -26
  2. package/package.json +1 -1
  3. package/service.mjs +29 -2
package/bridge.mjs CHANGED
@@ -32,7 +32,7 @@ import os from 'node:os'
32
32
  import fs from 'node:fs'
33
33
  import path from 'node:path'
34
34
  import readline from 'node:readline'
35
- import { spawn, execSync, execFileSync } from 'node:child_process'
35
+ import { spawn, execFileSync } from 'node:child_process'
36
36
  import { fileURLToPath } from 'node:url'
37
37
  import { createRequire } from 'node:module'
38
38
  import { randomUUID } from 'node:crypto'
@@ -232,8 +232,9 @@ if (argv[0] === 'install-service' || argv[0] === 'uninstall-service') {
232
232
  if (argv[0] === 'restart-service') {
233
233
  const arg1 = argv[1] || ''
234
234
  const svcRoom = (!arg1 || arg1.startsWith('-')) ? null : arg1.toUpperCase().trim()
235
- const svc = await import('./service.mjs'); svc.restartService(svcRoom)
236
- process.exit(0)
235
+ const svc = await import('./service.mjs')
236
+ const ok = argv.includes('--current') ? svc.restartService(svcRoom) : svc.updateService(svcRoom)
237
+ process.exit(ok ? 0 : 1)
237
238
  }
238
239
 
239
240
  // Account mode (Phase 1, docs/specs/2026-06-16-account-bridge.md): one bridge per
@@ -270,7 +271,6 @@ if (argv[0] === 'provider') { const { runProvider } = await import('./provider.m
270
271
  if ((!argv[0] || argv[0] === 'setup') && process.stdin.isTTY) {
271
272
  const { runLauncher, detectState } = await import('./launcher.mjs')
272
273
  const svc = await import('./service.mjs')
273
- const PKG_VERSION = (() => { try { return JSON.parse(fs.readFileSync(new URL('./package.json', import.meta.url), 'utf8')).version } catch { return 'latest' } })()
274
274
  const BRIDGE = fileURLToPath(import.meta.url)
275
275
  const io = {
276
276
  print: (s) => process.stderr.write(s + '\n'),
@@ -294,28 +294,8 @@ if ((!argv[0] || argv[0] === 'setup') && process.stdin.isTTY) {
294
294
  installService: ({ room = null, agentCmd } = {}) => { svc.installService(room, agentCmd ? [agentCmd] : []) },
295
295
  uninstallService: ({ room = null } = {}) => { svc.uninstallService(room) },
296
296
  restartService: ({ room = null } = {}) => { svc.restartService(room) },
297
- // "Restart & update the bridge" — resolve the NEWEST PUBLISHED version from npm and
298
- // re-pin the OS service to it (stale-proof: the rewritten launch wipes the npx cache
299
- // each start so it can't re-serve an old build). We must NOT trust PKG_VERSION here:
300
- // npx can run a STALE cached launcher, so this very menu may be an old version — that's
301
- // the bug that left the bridge stuck on 0.7.75. `npm view` hits the registry live, so it
302
- // updates correctly even from a stale launcher. Pinned to the resolved number — no auto-roll.
303
- restartUpdateService: ({ room = null } = {}) => {
304
- let target = null
305
- try { target = execSync('npm view thinkpool-pair version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() } catch { /* offline / no npm */ }
306
- if (!/^\d+\.\d+\.\d+/.test(target || '')) {
307
- // Can't reach npm to learn the newest version — DON'T silently re-pin this
308
- // launcher's PKG_VERSION (npx may have run a stale cached launcher, so it could
309
- // be OLDER than what's installed). Just RESTART the current pinned service (no
310
- // version change) and say so honestly — never call a same-version restart an
311
- // "update".
312
- process.stderr.write(`\n ⚠ couldn't reach npm to check for a newer version — restarting the current version, NO update applied. Try again when online.\n`)
313
- svc.restartService(room)
314
- return
315
- }
316
- process.stderr.write(`\n ◆ updating the background service to v${target} (newest published) and restarting…\n`)
317
- svc.installService(room, [], { version: target, staleProof: true }) // rewrites plist/unit at resolved version + reloads = update + restart
318
- },
297
+ // Menu and CLI share the same registry-resolve → repin → reload implementation.
298
+ restartUpdateService: ({ room = null } = {}) => { svc.updateService(room) },
319
299
  login: async () => { const { runLogin } = await import('./account.mjs'); await runLogin(SUPABASE_URL, SUPABASE_ANON, WEB_BASE) },
320
300
  // Provider config is NOT terminal — it writes provider.json and returns to the
321
301
  // menu so you can pick a model, switch back, or do something else. (It used to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.192",
3
+ "version": "0.7.193",
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
@@ -43,7 +43,12 @@ const shq = (s) => `'${String(s).replace(/'/g, `'\\''`)}'`
43
43
  // which is what left Max's account bridge stuck on 0.7.75 while @latest was 0.7.78.
44
44
  // `home` is the platform-correct, runtime-expanded home token ("$HOME" on darwin under
45
45
  // /bin/bash; "$$HOME" inside a systemd unit so systemd emits a literal $HOME to bash).
46
- const cacheWipe = (home) => `rm -rf ${home}/.npm/_npx/*/node_modules/thinkpool-pair 2>/dev/null`
46
+ // npx caches the temporary execution project as a unit. Removing only the package can
47
+ // leave its generated .bin/package-lock metadata behind, producing the contradictory
48
+ // state where `npm view` sees a version but `npx` reports ETARGET or no executable.
49
+ // The whole _npx directory is disposable; clear its children so the next launch is a
50
+ // genuinely clean install.
51
+ const cacheWipe = (home) => `rm -rf ${home}/.npm/_npx/* 2>/dev/null`
47
52
 
48
53
  // The version installing the service. By DEFAULT the service is pinned to this exact
49
54
  // version (not @latest) so a future bad npm publish can't auto-roll to every machine's
@@ -83,7 +88,7 @@ export function buildArtifact(platform, { room = null, cmdArgs = [], autoUpdate
83
88
  // 0.7.87). `--prefer-online` forces npx to REVALIDATE the packument against the
84
89
  // registry every resolve, so `@latest` always means the true latest. Only needed for
85
90
  // the @latest (autoUpdate) tier — a pinned exact version is immutable, no staleness.
86
- const onlineFlag = autoUpdate ? ['--prefer-online'] : []
91
+ const onlineFlag = (autoUpdate || staleProof) ? ['--prefer-online'] : []
87
92
  const pkgArgs = room ? ['-y', ...onlineFlag, verSpec, room] : ['-y', ...onlineFlag, verSpec]
88
93
  // In-process self-update (account.mjs / bridge.mjs) is gated on this env — only on when
89
94
  // the user explicitly opted into auto-update.
@@ -353,6 +358,28 @@ export function restartService(room) {
353
358
  }
354
359
  }
355
360
 
361
+ // One authoritative update path for both the interactive menu and `restart-service`.
362
+ // Resolve the registry version first, then rewrite + reload the service pinned to that
363
+ // exact build. If npm is unavailable, fail without pretending an old-version restart
364
+ // was an update.
365
+ export function updateService(room, { exec = execSync, install = installService, active = isServiceInstalled } = {}) {
366
+ if (!active(room)) {
367
+ process.stderr.write(' ⚠ no background service installed to update — install one first.\n')
368
+ return false
369
+ }
370
+ let target = ''
371
+ try {
372
+ target = String(exec('npm view thinkpool-pair version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })).trim()
373
+ } catch { /* reported below */ }
374
+ if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(target)) {
375
+ process.stderr.write(" ⚠ couldn't reach npm or resolve the published version — bridge left unchanged; NO update or restart was applied.\n")
376
+ return false
377
+ }
378
+ process.stderr.write(`\n ◆ updating the background service to v${target} and restarting…\n`)
379
+ install(room, [], { version: target, staleProof: true })
380
+ return true
381
+ }
382
+
356
383
  // Ground truth: is the OS service manager ACTUALLY running our service right now?
357
384
  // A leftover plist/unit on disk does NOT mean it's loaded — `launchctl load` can
358
385
  // silently no-op (the 2026-06-26 "says installed but it's off" bug), and a unit can