thinkpool-pair 0.7.308 → 0.7.309

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.308",
3
+ "version": "0.7.309",
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/reap-terminal.mjs CHANGED
@@ -36,21 +36,28 @@ export async function reapTerminalRow ({ id, token, supabaseUrl, anonKey, shutti
36
36
  if (!token) return { reaped: false, reason: 'anon' }
37
37
  if (!id) return { reaped: false, reason: 'no-id' }
38
38
  try {
39
- // Mirror of the client reap: DELETE code_terminals WHERE id = <terminal id>. The
40
- // terminal id IS the code_terminals PK (client/bridge-minted uuid). RLS scopes the
41
- // delete to this bridge's sessions, so id alone is sufficient + idempotent (a fast
42
- // client tab may have reaped first — 204 / 0 rows is fine).
43
- const response = await fetchImpl(`${supabaseUrl}/rest/v1/code_terminals?id=eq.${encodeURIComponent(id)}`, {
44
- method: 'DELETE',
45
- headers: { apikey: anonKey, Authorization: `Bearer ${token}` },
39
+ // A terminal at the 50k transcript cap cannot archive + cascade-delete inside
40
+ // the authenticated role's ordinary 8s statement timeout. The member-authorized
41
+ // RPC scopes a longer timeout to this one cleanup operation; a raw REST DELETE
42
+ // timed out and left the row as a cross-device dormant ghost (8TMX546Q).
43
+ const response = await fetchImpl(`${supabaseUrl}/rest/v1/rpc/delete_code_terminal`, {
44
+ method: 'POST',
45
+ headers: {
46
+ apikey: anonKey,
47
+ Authorization: `Bearer ${token}`,
48
+ 'Content-Type': 'application/json',
49
+ },
50
+ body: JSON.stringify({ p_terminal: id }),
46
51
  })
47
52
  // fetch resolves for HTTP failures. A 401/500 did not reap the row and must
48
53
  // stay visible to the fire-and-forget caller as a failed best-effort cleanup.
49
54
  if (!response?.ok) {
55
+ let detail = ''
56
+ try { detail = String(await response.text()).slice(0, 240) } catch { /* no body */ }
50
57
  return fail({
51
58
  reaped: false,
52
59
  reason: 'error',
53
- error: `HTTP ${response?.status ?? 'unknown'}${response?.statusText ? ` ${response.statusText}` : ''}`,
60
+ error: `HTTP ${response?.status ?? 'unknown'}${response?.statusText ? ` ${response.statusText}` : ''}${detail ? `: ${detail}` : ''}`,
54
61
  status: response?.status,
55
62
  })
56
63
  }
@@ -1,3 +1,5 @@
1
+ import { reapTerminalRow } from './reap-terminal.mjs'
2
+
1
3
  /* Reconcile durable terminal rows after a bridge has restored its on-disk roster.
2
4
 
3
5
  A clean close is reaped by reap-terminal.mjs. This module closes the other
@@ -48,13 +50,16 @@ export async function reconcileTerminalRows ({
48
50
 
49
51
  const reaped = []
50
52
  for (const id of stale) {
51
- const del = await fetchImpl(
52
- `${supabaseUrl}/rest/v1/code_terminals?id=eq.${encodeURIComponent(id)}` +
53
- `&host_id=eq.${encodeURIComponent(hostId)}`,
54
- { method: 'DELETE', headers: { apikey: anonKey, Authorization: `Bearer ${token}` } },
55
- )
56
- if (!del?.ok) {
57
- return fail({ reason: 'error', error: `delete HTTP ${del?.status ?? 'unknown'}`, status: del?.status, reaped })
53
+ const deletion = await reapTerminalRow({
54
+ id, token, supabaseUrl, anonKey, fetchImpl,
55
+ })
56
+ if (!deletion.reaped) {
57
+ return fail({
58
+ reason: 'error',
59
+ error: `delete ${deletion.error || deletion.reason}`,
60
+ status: deletion.status,
61
+ reaped,
62
+ })
58
63
  }
59
64
  reaped.push(id)
60
65
  }