thinkpool-pair 0.7.109 → 0.7.110

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/account.mjs +10 -2
  2. package/package.json +1 -1
package/account.mjs CHANGED
@@ -255,6 +255,7 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
255
255
  let applyRequested = false // a user clicked "apply" in some room (Slice 3)
256
256
  let pendingUpdate = null // newest published version once the poll sees it
257
257
  const warned = new Set()
258
+ const refused = new Map() // room -> reason ('home'|'none'|'dir-gone'): owned but NOT served, announced in presence so the web shows a precise "attach this session" card instead of spinning
258
259
 
259
260
  // Account presence — so the web dashboard can show "● Bridge active" for this
260
261
  // account without per-room probing. We TRACK this machine (name + served room
@@ -324,7 +325,7 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
324
325
  // service (always-on, survives reboot) vs a foreground `npx` run that dies with its
325
326
  // terminal. THINKPOOL_PAIR_AUTOUPDATE=1 is set ONLY by the install-service plist/unit
326
327
  // (service.mjs), so it's the reliable proxy for "running as the managed service".
327
- const pushPresence = () => trackPresence({ name: machine, version: VERSION, rooms: [...children.keys()], service: process.env.THINKPOOL_PAIR_AUTOUPDATE === '1', ts: Date.now() })
328
+ const pushPresence = () => trackPresence({ name: machine, version: VERSION, rooms: [...children.keys()], refused: [...refused].map(([code, reason]) => ({ code, reason })), service: process.env.THINKPOOL_PAIR_AUTOUPDATE === '1', ts: Date.now() })
328
329
  // Re-track on EVERY (re)subscribe, not just the first: a realtime reconnect
329
330
  // (network blip, or a token swap mid-flight) rejoins the channel and must
330
331
  // re-announce, or the dashboard would read "no bridge" until the next restart.
@@ -491,18 +492,23 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
491
492
  // participant's bridge is duplication the UI never even looks at.
492
493
  const { data } = await sb.from('code_sessions').select('code,name,last_active_at,owner_id,participants').eq('owner_id', session.user.id).order('last_active_at', { ascending: false }).limit(20)
493
494
  rooms = data || []
495
+ // Prune refused entries for rooms we no longer own (only on a SUCCESSFUL fetch,
496
+ // so a transient query error never wipes the set).
497
+ const ownedCodes = new Set(rooms.map((r) => r.code))
498
+ for (const c of [...refused.keys()]) if (!ownedCodes.has(c)) refused.delete(c)
494
499
  } catch { /* transient — keep existing children, retry next tick */ }
495
500
  const dirs = loadDirs()
496
501
  const served = loadServed()
497
502
  for (const r of rooms) {
498
503
  const room = r.code
499
- if (!room || children.has(room)) continue
504
+ if (!room || children.has(room)) { if (room) refused.delete(room); continue } // served → not refused
500
505
  if (r.owner_id && r.owner_id !== session.user.id) continue // never serve a room we don't own (defense-in-depth behind the .eq above)
501
506
  // Resolve the serve dir: explicit bind > last-served memory > launch dir. REFUSE
502
507
  // to auto-serve an unbound room from $HOME — that strips the agent's resume context
503
508
  // (cwd-keyed SDK sessions) and shows your home folder as the repo (FVIHV1DE).
504
509
  const choice = resolveServeDir({ bound: dirs[room], remembered: served[room], defaultDir: DEFAULT_DIR, home: os.homedir() })
505
510
  if (choice.refuse) {
511
+ refused.set(room, choice.reason)
506
512
  if (!warned.has(room)) {
507
513
  warned.add(room)
508
514
  const why = choice.reason === 'home'
@@ -514,10 +520,12 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
514
520
  }
515
521
  const dir = choice.dir
516
522
  if (!fs.existsSync(dir)) {
523
+ refused.set(room, 'dir-gone')
517
524
  if (!warned.has(room)) { warned.add(room); process.stderr.write(`\n ◇ ${room}: bound directory is gone (${dir}) — re-bind it.\n`) }
518
525
  continue
519
526
  }
520
527
  warned.delete(room)
528
+ refused.delete(room) // about to serve → clear any prior refusal
521
529
  // Learn this room → dir so a later relaunch from $HOME resumes it HERE (the real
522
530
  // repo) instead of the home-dir fallback. Only the auto path is remembered; an
523
531
  // explicit bind already persists in dirs.json.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.109",
3
+ "version": "0.7.110",
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": {