thinkpool-pair 0.7.55 → 0.7.56

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/account.mjs +12 -1
  2. package/bridge.mjs +21 -2
  3. package/package.json +1 -1
package/account.mjs CHANGED
@@ -186,6 +186,9 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
186
186
  }
187
187
  const { sb, session } = auth
188
188
  const email = session.user?.email || 'your account'
189
+ // Current owner JWT, handed to each room-bridge child for authed web writes
190
+ // (code-mockup, whip L16). Refreshed below; pushed to live children on rotation.
191
+ let currentAccessToken = session.access_token || null
189
192
 
190
193
  // Owner-gate (Contract C-CODE-2): hosting a code session — spawning the agent and
191
194
  // burning compute on this machine — requires Pro or Enterprise. This is the layer
@@ -253,7 +256,13 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
253
256
  const keepFresh = scheduleTokenRefresh({
254
257
  sb,
255
258
  session,
256
- onRefreshed: (s) => { saveAuth(s); pushPresence() },
259
+ onRefreshed: (s) => {
260
+ saveAuth(s); pushPresence()
261
+ // Push the rotated owner token to every live child so their authed writes
262
+ // (code-mockup) never go stale on a long session (whip L16).
263
+ currentAccessToken = s.access_token || currentAccessToken
264
+ for (const c of children.values()) { try { c.send({ t: 'token', accessToken: currentAccessToken }) } catch { /* child not ready */ } }
265
+ },
257
266
  forceMs: parseInt(process.env.TP_TOKEN_REFRESH_FORCE_MS, 10) || 0,
258
267
  })
259
268
 
@@ -287,6 +296,8 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
287
296
  // can't make a child self-exit; flag it as an account child instead.
288
297
  const env = { ...process.env, THINKPOOL_PAIR_ACCOUNT_CHILD: '1' }
289
298
  delete env.THINKPOOL_PAIR_AUTOUPDATE
299
+ // Hand the child the owner JWT for authed web writes (code-mockup, L16).
300
+ if (currentAccessToken) env.TP_ACCESS_TOKEN = currentAccessToken
290
301
  const child = spawn(process.execPath, [BRIDGE, room, '--headless', '--auto=claude'], { cwd: dir, stdio: ['inherit', 'inherit', 'inherit', 'ipc'], env })
291
302
  children.set(room, child)
292
303
  childIdle.set(room, false) // unknown until it reports → never restart a fresh child
package/bridge.mjs CHANGED
@@ -47,6 +47,14 @@ const SUPABASE_URL = process.env.TP_SUPABASE_URL || 'https://daytvtakmlixpfbbq
47
47
  const WEB_BASE = process.env.TP_WEB_BASE || 'https://thinkpool.io'
48
48
  const SUPABASE_ANON = process.env.TP_SUPABASE_ANON || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRheXR2dGFrbWxpeHBmYmJxempkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzgwNTMxNzEsImV4cCI6MjA5MzYyOTE3MX0.zYmz8bHUNEY4STUr2JuXChAsKwxNwkwFHe1Hd0Betqo'
49
49
 
50
+ // Owner access token for AUTHED web writes (currently /api/code-mockup, whip L16).
51
+ // The room-bridge is anon by design; the account supervisor (account.mjs) owns the
52
+ // session, so it hands a fresh owner token in at spawn (TP_ACCESS_TOKEN) and pushes
53
+ // a rotated one over IPC ({ t:'token' }) on each refresh. Stays null for a
54
+ // directly-started bridge with no account login — those writes go unattributed,
55
+ // never owner-spoofed (the server treats a missing/!owner token accordingly).
56
+ let codeAuthToken = process.env.TP_ACCESS_TOKEN || null
57
+
50
58
  // Per-terminal rolling scrollback (raw chars). Replayed to joining clients.
51
59
  // Kept comfortably under realtime payload limits once base64'd.
52
60
  const SCROLLBACK_MAX = 120_000
@@ -518,9 +526,14 @@ const handleManifest = async (box, file, term) => {
518
526
  const readTxt = (p) => { try { return p && fs.readFileSync(p, 'utf8') } catch { return null } }
519
527
  const cid = randomUUID()
520
528
  try {
529
+ const mockupHeaders = { 'Content-Type': 'application/json' }
530
+ // Authenticate as the owner so the server can attribute the persisted row to a
531
+ // verified user instead of trusting "knows the code" (whip L16). Omitted when
532
+ // we have no token (direct bridge, no login) → server persists it unattributed.
533
+ if (codeAuthToken) mockupHeaders.Authorization = `Bearer ${codeAuthToken}`
521
534
  const res = await fetch(`${WEB_BASE}/api/code-mockup`, {
522
535
  method: 'POST',
523
- headers: { 'Content-Type': 'application/json' },
536
+ headers: mockupHeaders,
524
537
  body: JSON.stringify({
525
538
  code: room, slug: m.slug, title: m.title, cid, ts: m.ts, term,
526
539
  desktopPng: readB64(m.desktop), mobilePng: readB64(m.mobile), html: readTxt(m.html),
@@ -1206,7 +1219,13 @@ if (process.env.THINKPOOL_PAIR_AUTOUPDATE === '1' && VERSION) {
1206
1219
  // Contract #1) + `idle` (the quiet window, for the unattended fallback) + whether a
1207
1220
  // web peer is present, so the supervisor restarts on an explicit click between turns
1208
1221
  // and otherwise waits for true idle with nobody watching (Slice 3).
1209
- process.on('message', (m) => { if (m && m.t === 'update-available') surfaceUpdate(m.version) })
1222
+ process.on('message', (m) => {
1223
+ if (!m) return
1224
+ if (m.t === 'update-available') surfaceUpdate(m.version)
1225
+ // Supervisor rotated the owner JWT (account.mjs scheduleTokenRefresh) — adopt
1226
+ // it so our authed writes (code-mockup) never go stale on a long session (L16).
1227
+ else if (m.t === 'token') codeAuthToken = m.accessToken || null
1228
+ })
1210
1229
  setInterval(() => { try { process.send({ t: 'idle', idle: idleNow(), between: betweenTurns(), webPeer: webPeerPresent() }) } catch { /* parent gone */ } }, 5000).unref()
1211
1230
  } else if (VERSION) {
1212
1231
  // Foreground run (not the supervised service): it can't self-restart, but
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.55",
3
+ "version": "0.7.56",
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": {