thinkpool-pair 0.7.22 → 0.7.23

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
@@ -36,7 +36,7 @@ import { spawn } from 'node:child_process'
36
36
  import { randomUUID } from 'node:crypto'
37
37
  import { createClient } from '@supabase/supabase-js'
38
38
  import { startClaudeSession } from './claude-session.mjs'
39
- import { saveSession, flushSession, deleteSession, loadAll, canResume, loadPtyId, savePtyId } from './session-store.mjs'
39
+ import { saveSession, flushSession, deleteSession, loadAll, canResume, loadPtyId, savePtyId, loadNames, saveNames } from './session-store.mjs'
40
40
 
41
41
  // Public client creds (the same anon values the web app ships — safe to embed).
42
42
  // Override with TP_SUPABASE_URL / TP_SUPABASE_ANON if you ever need to.
@@ -331,6 +331,11 @@ const bcast = (event, payload) => {
331
331
  } catch { /* noop */ }
332
332
  }
333
333
 
334
+ // Per-room terminal display names (id -> label), set by the web's `term-rename`.
335
+ // Persisted on the host so a rename is cross-device + survives a bridge restart;
336
+ // every announce carries them so a late-joining or second device sees them too.
337
+ const termNames = loadNames(room)
338
+
334
339
  const announce = () =>
335
340
  bcast('bridge', {
336
341
  v: 2, name, repo: repoLabel, branch,
@@ -352,10 +357,12 @@ const announce = () =>
352
357
  // cols/rows: the PTY's one true size — web viewers render this grid and
353
358
  // scale it to their own page instead of voting to reflow it.
354
359
  terms: [
355
- ...[...terms.entries()].map(([id, t]) => ({ id, cmd: t.cmd, alive: true, cols: t.term.cols, rows: t.term.rows })),
360
+ ...[...terms.entries()].map(([id, t]) => ({ id, cmd: t.cmd, alive: true, cols: t.term.cols, rows: t.term.rows, name: termNames[id] || undefined })),
356
361
  // Structured sessions advertise kind:'structured' so the web renders the
357
- // reader (not xterm) and drives them with code-turn / code-perm.
358
- ...[...sessions.entries()].map(([id, s]) => ({ id, cmd: s.cmd, kind: 'structured', alive: true, commands: s.commands })),
362
+ // reader (not xterm) and drives them with code-turn / code-perm. `mode` +
363
+ // `name` ride along so a second device shows the live permission mode and
364
+ // the chosen label instead of defaulting to Normal / unnamed.
365
+ ...[...sessions.entries()].map(([id, s]) => ({ id, cmd: s.cmd, kind: 'structured', alive: true, commands: s.commands, mode: s.mode || undefined, name: termNames[id] || undefined })),
359
366
  ],
360
367
  })
361
368
 
@@ -820,6 +827,16 @@ channel
820
827
  .on('broadcast', { event: 'code-close' }, ({ payload }) => {
821
828
  endStructured(payload?.id)
822
829
  })
830
+ // Persist + re-announce terminal renames. The web also echoes term-rename to
831
+ // online peers directly; storing it here is what reaches a device that joins
832
+ // LATER (or a second machine) — those only ever see the announce.
833
+ .on('broadcast', { event: 'term-rename' }, ({ payload }) => {
834
+ if (!payload?.id) return
835
+ if (payload.name) termNames[payload.id] = String(payload.name).slice(0, 80)
836
+ else delete termNames[payload.id]
837
+ saveNames(room, termNames)
838
+ announce()
839
+ })
823
840
  .on('broadcast', { event: 'who' }, announce)
824
841
  .subscribe(status => {
825
842
  if (status === 'SUBSCRIBED') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.22",
3
+ "version": "0.7.23",
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/session-store.mjs CHANGED
@@ -83,3 +83,17 @@ export function loadPtyId(room) {
83
83
  export function savePtyId(room, id) {
84
84
  try { ensureDir(room); fs.writeFileSync(ptyFile(room), String(id)) } catch { /* noop */ }
85
85
  }
86
+
87
+ // Per-room terminal display names (terminal id -> label), set via the web's
88
+ // `term-rename` broadcast. This is what makes a rename CROSS-DEVICE + survive a
89
+ // bridge restart: names were previously web-localStorage only + a live broadcast,
90
+ // so a device that joined later (or a second machine) saw none of them. The bridge
91
+ // stores them here and includes them in every announce. Stored as a single dotfile
92
+ // (NOT *.json) so it never lands in listRecs/loadAll.
93
+ const namesFile = (room) => path.join(dir(room), '.names')
94
+ export function loadNames(room) {
95
+ try { return JSON.parse(fs.readFileSync(namesFile(room), 'utf8')) || {} } catch { return {} }
96
+ }
97
+ export function saveNames(room, names) {
98
+ try { ensureDir(room); fs.writeFileSync(namesFile(room), JSON.stringify(names || {})) } catch { /* noop */ }
99
+ }