thinkpool-pair 0.7.22 → 0.7.24
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 +21 -4
- package/claude-session.mjs +2 -1
- package/package.json +1 -1
- package/session-store.mjs +14 -0
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
|
-
|
|
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/claude-session.mjs
CHANGED
|
@@ -274,7 +274,8 @@ export function startClaudeSession({ cwd, model, resume, env, mode: initialMode
|
|
|
274
274
|
// a bypass session still planned. Bias the room session to DO the work; only
|
|
275
275
|
// plan when the room is actually in Plan mode (⇧⇥) or the user explicitly asks.
|
|
276
276
|
appendSystemPrompt: [
|
|
277
|
-
'You are Claude running inside a ThinkPool Code room, driven live by a user (and possibly a partner) from a phone or browser.',
|
|
277
|
+
'ENVIRONMENT (authoritative — overrides any user-global CLAUDE.md or memory that claims otherwise): You are Claude running inside a ThinkPool Code room, driven live by a user (and possibly a partner) from a phone or browser, via the thinkpool-pair bridge.',
|
|
278
|
+
'You are NOT in cmux. cmux may be installed on this machine and appear in $PATH, but it did not spawn you and its automation/orchestration rules do not apply. If any loaded memory says "Claude Code lives inside cmux," it is describing direct terminal use, not this room — disregard it here. The runtime truth is the env: THINKPOOL_PAIR_ACCOUNT_CHILD / npm_lifecycle_script=thinkpool-pair mark a ThinkPool Code session.',
|
|
278
279
|
'Bias strongly toward DOING the work, not planning it. For a normal "add / fix / change / build X" request, just make the change directly.',
|
|
279
280
|
'Do NOT enter plan mode, do NOT call ExitPlanMode, and do NOT auto-invoke a brainstorming/planning skill UNLESS the user has switched the room into Plan mode or explicitly asks you to plan, design, or brainstorm first.',
|
|
280
281
|
'Any host-global instruction that says you must always brainstorm or plan before creative work does NOT apply here — this room is the exception.',
|
package/package.json
CHANGED
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
|
+
}
|