thinkpool-pair 0.7.4 → 0.7.6
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/account.mjs +19 -11
- package/package.json +1 -1
package/account.mjs
CHANGED
|
@@ -99,7 +99,8 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
99
99
|
process.exit(1)
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
process.
|
|
102
|
+
const DEFAULT_DIR = process.cwd() // unbound sessions auto-serve here; `bind` overrides per session
|
|
103
|
+
process.stderr.write(`\n ◆ thinkpool-pair — account mode · ${email}\n Auto-serving your sessions from ${DEFAULT_DIR}\n (point one at another repo: npx thinkpool-pair bind <code> <dir>)\n`)
|
|
103
104
|
|
|
104
105
|
const children = new Map() // room -> child process
|
|
105
106
|
const warned = new Set()
|
|
@@ -108,31 +109,29 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
108
109
|
// account without per-room probing. We TRACK this machine (name + served room
|
|
109
110
|
// codes + version) on a per-account channel; the dashboard subscribes read-only.
|
|
110
111
|
const machine = os.hostname().replace(/\.local$/, '')
|
|
111
|
-
const acct = sb.channel(`tpacct:${session.user.id}`, { config: { presence: { key: machine } } })
|
|
112
|
+
const acct = sb.channel(`tpacct:${session.user.id}`, { config: { presence: { key: machine }, broadcast: { self: false } } })
|
|
113
|
+
// Dashboard "Disconnect" → broadcast shutdown → clean exit (presence leaves, bar flips live).
|
|
114
|
+
acct.on('broadcast', { event: 'shutdown' }, () => { process.stderr.write('\n ◇ disconnect requested from the dashboard — stopping bridge.\n'); process.kill(process.pid, 'SIGTERM') })
|
|
112
115
|
const pushPresence = () => { try { acct.track({ name: machine, version: VERSION, rooms: [...children.keys()], ts: Date.now() }) } catch { /* noop */ } }
|
|
113
116
|
await new Promise((res) => acct.subscribe((st) => { if (st === 'SUBSCRIBED') { pushPresence(); res() } }))
|
|
114
117
|
|
|
115
118
|
const tick = async () => {
|
|
116
119
|
let rooms = []
|
|
117
120
|
try {
|
|
118
|
-
const { data } = await sb.from('code_sessions').select('code,name,last_active_at').order('last_active_at', { ascending: false })
|
|
121
|
+
const { data } = await sb.from('code_sessions').select('code,name,last_active_at').order('last_active_at', { ascending: false }).limit(20)
|
|
119
122
|
rooms = data || []
|
|
120
123
|
} catch { /* transient — keep existing children, retry next tick */ }
|
|
121
124
|
const dirs = loadDirs()
|
|
122
125
|
for (const r of rooms) {
|
|
123
126
|
const room = r.code
|
|
124
127
|
if (!room || children.has(room)) continue
|
|
125
|
-
const dir = dirs[room]
|
|
126
|
-
if (!dir) {
|
|
127
|
-
if (!warned.has(room)) { warned.add(room); process.stderr.write(`\n ◇ session ${r.name ? `"${r.name}" ` : ''}(${room}) needs a directory:\n npx thinkpool-pair bind ${room} <path>\n`) }
|
|
128
|
-
continue
|
|
129
|
-
}
|
|
128
|
+
const dir = dirs[room] || DEFAULT_DIR // auto-serve unbound sessions in the launch dir
|
|
130
129
|
if (!fs.existsSync(dir)) {
|
|
131
130
|
if (!warned.has(room)) { warned.add(room); process.stderr.write(`\n ◇ ${room}: bound directory is gone (${dir}) — re-bind it.\n`) }
|
|
132
131
|
continue
|
|
133
132
|
}
|
|
134
133
|
warned.delete(room)
|
|
135
|
-
process.stderr.write(`\n ◆ serving ${room}${r.name ? ` "${r.name}"` : ''} → ${dir}\n`)
|
|
134
|
+
process.stderr.write(`\n ◆ serving ${room}${r.name ? ` "${r.name}"` : ''} → ${dir}${dirs[room] ? '' : ' (default)'}\n`)
|
|
136
135
|
const child = spawn(process.execPath, [BRIDGE, room, '--headless'], { cwd: dir, stdio: 'inherit' })
|
|
137
136
|
children.set(room, child)
|
|
138
137
|
child.on('exit', () => { children.delete(room) }) // re-served on the next tick
|
|
@@ -142,7 +141,16 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
142
141
|
|
|
143
142
|
await tick()
|
|
144
143
|
const iv = setInterval(tick, 15000)
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
let stopping = false
|
|
145
|
+
const stop = (sig) => {
|
|
146
|
+
if (stopping) return; stopping = true
|
|
147
|
+
clearInterval(iv)
|
|
148
|
+
for (const c of children.values()) { try { c.kill(sig || 'SIGTERM') } catch { /* noop */ } }
|
|
149
|
+
// Flush the presence LEAVE before exiting so the dashboard flips to
|
|
150
|
+
// "not connected" in realtime (don't fire-and-forget the untrack).
|
|
151
|
+
;(async () => { try { await acct.untrack() } catch { /* noop */ } try { await sb.removeChannel(acct) } catch { /* noop */ } process.exit(0) })()
|
|
152
|
+
setTimeout(() => process.exit(0), 1500) // hard backstop if the flush hangs
|
|
153
|
+
}
|
|
154
|
+
for (const sig of ['SIGINT', 'SIGTERM', 'SIGHUP']) process.on(sig, () => stop(sig))
|
|
147
155
|
await new Promise(() => {})
|
|
148
156
|
}
|