thinkpool-pair 0.6.20 → 0.6.21

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/bridge.mjs CHANGED
@@ -211,6 +211,32 @@ if (dashIdx >= 0) {
211
211
  if (continuing) attachedArgs = [...agent.resume.args]
212
212
  }
213
213
  }
214
+
215
+ // First-run offer: install as a background service (auto-updating, survives
216
+ // reboot/logout) instead of this foreground run. Asked once per room on an
217
+ // interactive TTY, AFTER the agent is chosen so the service runs that exact
218
+ // command non-interactively. Skipped when running AS the service
219
+ // (AUTOUPDATE=1), headless, or non-interactive. Choice is remembered.
220
+ if (process.stdin.isTTY && !headless && process.env.THINKPOOL_PAIR_AUTOUPDATE !== '1') {
221
+ const svc = await import('./service.mjs')
222
+ if (svc.isServiceInstalled(room)) {
223
+ process.stderr.write(`\n ◆ a background service is already running room ${room} (auto-updating).\n ◆ remove it with: npx thinkpool-pair@latest uninstall-service ${room}\n\n`)
224
+ process.exit(0)
225
+ }
226
+ const declineFile = path.join(os.homedir(), '.thinkpool-pair', `${room}.svc-declined`)
227
+ let declined = false
228
+ try { declined = fs.existsSync(declineFile) } catch { /* noop */ }
229
+ if (!declined) {
230
+ const yes = await askYesNo(`\n Keep this bridge running with auto-updates (survives reboot, no terminal to babysit)?\n Install it as a background service? [Y/n] `)
231
+ if (yes) {
232
+ svc.installService(room, attachedCmd ? [attachedCmd, ...attachedArgs] : [])
233
+ process.stderr.write(` ◆ the background service is handling room ${room} now — you can close this terminal.\n\n`)
234
+ process.exit(0) // hand off to the supervised service (no double bridge)
235
+ }
236
+ try { fs.mkdirSync(path.dirname(declineFile), { recursive: true }); fs.writeFileSync(declineFile, new Date().toISOString()) } catch { /* noop */ }
237
+ process.stderr.write(` ◆ running in this terminal. (Auto-update service anytime: npx thinkpool-pair@latest install-service ${room})\n`)
238
+ }
239
+ }
214
240
  const name = process.env.TP_NAME || os.userInfo().username || 'host'
215
241
 
216
242
  // Repo awareness — the room shows which project this machine is sharing.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.6.20",
3
+ "version": "0.6.21",
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
@@ -108,6 +108,13 @@ export function installService(room, cmdArgs = []) {
108
108
  process.stderr.write(` ◆ ${a.note}\n ◆ ${autoUpdate}\n ◆ logs: ${path.join(a.logDir, `${room}.log`)}\n ◆ remove with: thinkpool-pair uninstall-service ${room}\n\n`)
109
109
  }
110
110
 
111
+ // Is a boot-persistent service already installed for this room? (artifact file
112
+ // present.) Lets the main `npx thinkpool-pair <room>` run skip the offer + avoid
113
+ // starting a second bridge that would race the service on the same room.
114
+ export function isServiceInstalled(room) {
115
+ try { return fs.existsSync(buildArtifact(process.platform, { room }).file) } catch { return false }
116
+ }
117
+
111
118
  export function uninstallService(room) {
112
119
  const plat = process.platform
113
120
  let a