thinkpool-pair 0.7.266 → 0.7.267

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
@@ -695,7 +695,10 @@ async function refreshOwnerPlan() {
695
695
  // unit-tested reap-terminal.mjs. Fire-and-forget from endStructured — the module never
696
696
  // throws (best-effort), so an unawaited promise can't reject.
697
697
  function reapTerminalRow(id) {
698
- return _reapTerminalRow({ id, token: codeAuthToken, supabaseUrl: SUPABASE_URL, anonKey: SUPABASE_ANON, shuttingDown })
698
+ return _reapTerminalRow({
699
+ id, token: codeAuthToken, supabaseUrl: SUPABASE_URL, anonKey: SUPABASE_ANON, shuttingDown,
700
+ onFailure: ({ error, status }) => process.stderr.write(`\n ⚠ terminal row reap failed (${String(id).slice(0, 8)}${status ? `, HTTP ${status}` : ''}): ${error || 'unknown error'}\n`),
701
+ })
699
702
  }
700
703
  // Auth: is the sender a PARTICIPANT of THIS room (owner OR granted OR joined)? Room mode
701
704
  // serves the room, so RLS returns participants; we confirm the jwt's user is one of them.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.266",
3
+ "version": "0.7.267",
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
@@ -24,10 +24,14 @@
24
24
  // the feature, not the leak.
25
25
 
26
26
  /**
27
- * @returns {Promise<{reaped: boolean, reason: 'ok'|'shutdown'|'anon'|'no-id'|'error', error?: string}>}
27
+ * @returns {Promise<{reaped: boolean, reason: 'ok'|'shutdown'|'anon'|'no-id'|'error', error?: string, status?: number}>}
28
28
  * Never throws — best-effort; the client reap (#205) is the fallback.
29
29
  */
30
- export async function reapTerminalRow ({ id, token, supabaseUrl, anonKey, shuttingDown = false, fetchImpl = fetch }) {
30
+ export async function reapTerminalRow ({ id, token, supabaseUrl, anonKey, shuttingDown = false, fetchImpl = fetch, onFailure } = {}) {
31
+ const fail = (outcome) => {
32
+ try { onFailure?.(outcome) } catch { /* observability must not break a close */ }
33
+ return outcome
34
+ }
31
35
  if (shuttingDown) return { reaped: false, reason: 'shutdown' }
32
36
  if (!token) return { reaped: false, reason: 'anon' }
33
37
  if (!id) return { reaped: false, reason: 'no-id' }
@@ -36,12 +40,22 @@ export async function reapTerminalRow ({ id, token, supabaseUrl, anonKey, shutti
36
40
  // terminal id IS the code_terminals PK (client/bridge-minted uuid). RLS scopes the
37
41
  // delete to this bridge's sessions, so id alone is sufficient + idempotent (a fast
38
42
  // client tab may have reaped first — 204 / 0 rows is fine).
39
- await fetchImpl(`${supabaseUrl}/rest/v1/code_terminals?id=eq.${encodeURIComponent(id)}`, {
43
+ const response = await fetchImpl(`${supabaseUrl}/rest/v1/code_terminals?id=eq.${encodeURIComponent(id)}`, {
40
44
  method: 'DELETE',
41
45
  headers: { apikey: anonKey, Authorization: `Bearer ${token}` },
42
46
  })
47
+ // fetch resolves for HTTP failures. A 401/500 did not reap the row and must
48
+ // stay visible to the fire-and-forget caller as a failed best-effort cleanup.
49
+ if (!response?.ok) {
50
+ return fail({
51
+ reaped: false,
52
+ reason: 'error',
53
+ error: `HTTP ${response?.status ?? 'unknown'}${response?.statusText ? ` ${response.statusText}` : ''}`,
54
+ status: response?.status,
55
+ })
56
+ }
43
57
  return { reaped: true, reason: 'ok' }
44
58
  } catch (e) {
45
- return { reaped: false, reason: 'error', error: e?.message }
59
+ return fail({ reaped: false, reason: 'error', error: e?.message })
46
60
  }
47
61
  }