thinkpool-pair 0.7.5 → 0.7.7
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 +15 -4
- package/bridge.mjs +7 -3
- package/package.json +1 -1
package/account.mjs
CHANGED
|
@@ -109,7 +109,9 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
109
109
|
// account without per-room probing. We TRACK this machine (name + served room
|
|
110
110
|
// codes + version) on a per-account channel; the dashboard subscribes read-only.
|
|
111
111
|
const machine = os.hostname().replace(/\.local$/, '')
|
|
112
|
-
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') })
|
|
113
115
|
const pushPresence = () => { try { acct.track({ name: machine, version: VERSION, rooms: [...children.keys()], ts: Date.now() }) } catch { /* noop */ } }
|
|
114
116
|
await new Promise((res) => acct.subscribe((st) => { if (st === 'SUBSCRIBED') { pushPresence(); res() } }))
|
|
115
117
|
|
|
@@ -130,7 +132,7 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
130
132
|
}
|
|
131
133
|
warned.delete(room)
|
|
132
134
|
process.stderr.write(`\n ◆ serving ${room}${r.name ? ` "${r.name}"` : ''} → ${dir}${dirs[room] ? '' : ' (default)'}\n`)
|
|
133
|
-
const child = spawn(process.execPath, [BRIDGE, room, '--headless'], { cwd: dir, stdio: 'inherit' })
|
|
135
|
+
const child = spawn(process.execPath, [BRIDGE, room, '--headless', '--auto=claude'], { cwd: dir, stdio: 'inherit' })
|
|
134
136
|
children.set(room, child)
|
|
135
137
|
child.on('exit', () => { children.delete(room) }) // re-served on the next tick
|
|
136
138
|
}
|
|
@@ -139,7 +141,16 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
139
141
|
|
|
140
142
|
await tick()
|
|
141
143
|
const iv = setInterval(tick, 15000)
|
|
142
|
-
|
|
143
|
-
|
|
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))
|
|
144
155
|
await new Promise(() => {})
|
|
145
156
|
}
|
package/bridge.mjs
CHANGED
|
@@ -175,6 +175,9 @@ if (_superOwn.includes('--supervise') || _superOwn.includes('--keep-alive')) {
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
const headless = argv.includes('--headless')
|
|
178
|
+
// Account-mode children pass --auto=<agent> so a served session opens a live
|
|
179
|
+
// terminal automatically (headless, no TTY/picker) instead of an empty room.
|
|
180
|
+
const autoAgent = (argv.find(a => a.startsWith('--auto=')) || '').slice(7) || null
|
|
178
181
|
// Structured mode (Phase 2, opt-in): Claude Code runs through the Agent SDK
|
|
179
182
|
// (structured events + risk-tiered permission gate) instead of the PTY byte
|
|
180
183
|
// relay. Default OFF — the PTY path is untouched. Only applies to `claude`.
|
|
@@ -694,16 +697,17 @@ channel
|
|
|
694
697
|
if (status === 'SUBSCRIBED') {
|
|
695
698
|
realtimeHealthy = true; brokenSince = 0
|
|
696
699
|
channel.track({ name, role: 'bridge' })
|
|
697
|
-
|
|
700
|
+
const startCmd = attachedCmd || autoAgent // autoAgent: account-mode auto-open (headless)
|
|
701
|
+
if (startCmd && !terms.size && !sessions.size) {
|
|
698
702
|
// Claude + structured mode → Agent SDK session; everything else → PTY.
|
|
699
703
|
// On restart, restore the latest saved structured session for this room:
|
|
700
704
|
// replay its transcript + resume the live SDK context if recent enough.
|
|
701
|
-
if (wantStructured(
|
|
705
|
+
if (wantStructured(startCmd)) {
|
|
702
706
|
const prev = loadLatest(room)
|
|
703
707
|
if (prev && (prev.log?.length || prev.sessionId)) openStructured({ id: prev.id, resume: canResume(prev) ? prev.sessionId : undefined, log: prev.log, commands: prev.commands })
|
|
704
708
|
else openStructured({ id: randomUUID() })
|
|
705
709
|
}
|
|
706
|
-
else openTerm({ id: randomUUID(), cmd:
|
|
710
|
+
else openTerm({ id: randomUUID(), cmd: startCmd, args: attachedArgs, attached: !autoAgent })
|
|
707
711
|
}
|
|
708
712
|
announce()
|
|
709
713
|
process.stderr.write(headless
|