thinkpool-pair 0.7.293 → 0.7.295
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 +10 -3
- package/bridge.mjs +12 -5
- package/launcher.mjs +10 -1
- package/package.json +1 -1
package/account.mjs
CHANGED
|
@@ -416,6 +416,7 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
416
416
|
const childIdle = new Map() // room -> bool (last idle report: in the quiet window)
|
|
417
417
|
const childBetween = new Map() // room -> bool (between turns now — safe to restart per Contract #1)
|
|
418
418
|
const childPeer = new Map() // room -> bool (a web client is watching this room)
|
|
419
|
+
const readyRooms = new Set() // child subscribed + restored + announced usable room state
|
|
419
420
|
// Thinkpool Ensemble cross-ROOM router (Tier 1): the supervisor is the only process
|
|
420
421
|
// that knows every room on this machine, so it routes a child's read_session into the
|
|
421
422
|
// target room's child and relays the reply back. roomNames feeds list_sessions; pairPending
|
|
@@ -562,7 +563,7 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
562
563
|
// to spawn against — re-read each announce so an add/remove reflects immediately.
|
|
563
564
|
// Both are additive; older clients ignore them (feature: multi-provider BYOK, slice 1).
|
|
564
565
|
const PROVIDER_PUBKEY = publicKeyB64()
|
|
565
|
-
const pushPresence = () => trackPresence({ name: machine, bridgeId: BRIDGE_ID, version: VERSION, rooms: [...children.keys()], refused: [...refused].map(([code, reason]) => ({ code, reason })), service: runsAsService(), providerPubKey: PROVIDER_PUBKEY, providers: announceProviders(), agents: installedAgentCommands().map((cmd) => ({ cmd })), ts: Date.now() })
|
|
566
|
+
const pushPresence = () => trackPresence({ name: machine, bridgeId: BRIDGE_ID, version: VERSION, rooms: [...children.keys()], readyRooms: [...readyRooms], refused: [...refused].map(([code, reason]) => ({ code, reason })), service: runsAsService(), providerPubKey: PROVIDER_PUBKEY, providers: announceProviders(), agents: installedAgentCommands().map((cmd) => ({ cmd })), ts: Date.now() })
|
|
566
567
|
|
|
567
568
|
// ── Provider registry — the multi-BYOK wire contract (slice 1). ─────────────
|
|
568
569
|
// AUTH: the account channel `tpacct:<uid>` is created WITHOUT config.private:true,
|
|
@@ -895,12 +896,18 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
895
896
|
console.log(`child_spawn sup=${SUP_ID} room=${room} pid=${child.pid} dir=${dir} had_prior=${alreadyHad}`)
|
|
896
897
|
|
|
897
898
|
children.set(room, child)
|
|
899
|
+
readyRooms.delete(room)
|
|
898
900
|
childIdle.set(room, false) // unknown until it reports → never restart a fresh child
|
|
899
901
|
childBetween.set(room, false)
|
|
900
902
|
childPeer.set(room, false)
|
|
901
903
|
child.on('message', (m) => {
|
|
902
904
|
if (!m) return
|
|
903
|
-
if (m.t === '
|
|
905
|
+
if (m.t === 'room-ready') {
|
|
906
|
+
readyRooms.add(room)
|
|
907
|
+
console.log(`child_ready sup=${SUP_ID} room=${room} pid=${child.pid}`)
|
|
908
|
+
pushPresence()
|
|
909
|
+
}
|
|
910
|
+
else if (m.t === 'idle') { childIdle.set(room, !!m.idle); childBetween.set(room, m.between != null ? !!m.between : !!m.idle); childPeer.set(room, !!m.webPeer) }
|
|
904
911
|
else if (m.t === 'apply-update') { applyRequested = true; applyIfIdle() } // user clicked the chip
|
|
905
912
|
// ── Thinkpool Ensemble cross-ROOM routing ──────────────────────────────
|
|
906
913
|
// `room` here is the room that sent the message (this closure is per-child).
|
|
@@ -993,7 +1000,7 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
993
1000
|
// ── supervisor child_exit logging (cascade brg-instrument) ─────────────────
|
|
994
1001
|
console.log(`child_exit sup=${SUP_ID} room=${room} pid=${child?.pid || '?'} code=${code || '?'}`)
|
|
995
1002
|
|
|
996
|
-
children.delete(room); childIdle.delete(room); childBetween.delete(room); childPeer.delete(room); roomNames.delete(room); roomPartner.delete(room) // re-served on the next tick
|
|
1003
|
+
children.delete(room); readyRooms.delete(room); childIdle.delete(room); childBetween.delete(room); childPeer.delete(room); roomNames.delete(room); roomPartner.delete(room) // re-served on the next tick
|
|
997
1004
|
// Fail any in-flight cross-room peeks/posts this dead room had ASKED (bus-origin
|
|
998
1005
|
// entries have askerRoom null and just time out on their own).
|
|
999
1006
|
for (const [reqId, w] of pairPending) { if (w?.askerRoom === room) pairPending.delete(reqId) }
|
package/bridge.mjs
CHANGED
|
@@ -348,9 +348,9 @@ if ((!argv[0] || argv[0] === 'setup') && process.stdin.isTTY) {
|
|
|
348
348
|
child.on('exit', (code) => process.exit(code == null ? 0 : code))
|
|
349
349
|
}),
|
|
350
350
|
serveAccountForeground: async () => { const { runAccount } = await import('./account.mjs'); await runAccount(SUPABASE_URL, SUPABASE_ANON) },
|
|
351
|
-
// install / uninstall
|
|
352
|
-
//
|
|
353
|
-
//
|
|
351
|
+
// install / uninstall are NOT terminal — they do their work, print their notes,
|
|
352
|
+
// and return to the menu. A confirmed update closes the launcher instead: its
|
|
353
|
+
// in-memory package version is stale while the new service runs independently.
|
|
354
354
|
installService: ({ room = null, agentCmd } = {}) => { svc.installService(room, agentCmd ? [agentCmd] : []) },
|
|
355
355
|
uninstallService: ({ room = null } = {}) => { svc.uninstallService(room) },
|
|
356
356
|
restartService: ({ room = null } = {}) => { svc.restartService(room) },
|
|
@@ -1117,7 +1117,7 @@ const announce = () => {
|
|
|
1117
1117
|
// on a custom provider without the owner's account-channel registry.
|
|
1118
1118
|
const provNames = providerNameMap()
|
|
1119
1119
|
const rev = ++announceRev
|
|
1120
|
-
return
|
|
1120
|
+
return bcastAwait('bridge', {
|
|
1121
1121
|
v: 2, name, bridge_id: BRIDGE_ID, started_at: BRIDGE_STARTED_AT, rev, repo: repoLabel, branch: readBranch(),
|
|
1122
1122
|
// sdkWarn: the auto-pulled agent SDK failed its boot compatibility smoke test —
|
|
1123
1123
|
// surfaced so the room can show a banner (turns may misbehave; pin a good SDK).
|
|
@@ -4459,7 +4459,14 @@ channel
|
|
|
4459
4459
|
openTerm({ id: ptyId, cmd: startCmd, args: attachedArgs, attached: !autoAgent })
|
|
4460
4460
|
}
|
|
4461
4461
|
}
|
|
4462
|
-
announce()
|
|
4462
|
+
await announce()
|
|
4463
|
+
// Account presence must distinguish a spawned child from a usable room.
|
|
4464
|
+
// Signal only after realtime subscribed, durable sessions restored, and the
|
|
4465
|
+
// first authoritative roster was announced. The dashboard keeps the bridge
|
|
4466
|
+
// in "Restarting" until every pre-restart room reaches this boundary.
|
|
4467
|
+
if (process.send && process.env.THINKPOOL_PAIR_ACCOUNT_CHILD === '1') {
|
|
4468
|
+
try { process.send({ t: 'room-ready', room, bridgeId: BRIDGE_ID }) } catch { /* supervisor exited */ }
|
|
4469
|
+
}
|
|
4463
4470
|
process.stderr.write(headless
|
|
4464
4471
|
? `\n ◆ thinkpool — relaying room ${room} (headless). Open terminals from the web UI.\n\n`
|
|
4465
4472
|
: `\n ◆ thinkpool — sharing "${attachedCmd}"${continuing ? ' (continuing your latest session)' : ''} into room ${room}. Open the web UI and you're both in.\n\n`)
|
package/launcher.mjs
CHANGED
|
@@ -278,7 +278,16 @@ export async function runLauncher({ actions, io, state = detectState(), refresh
|
|
|
278
278
|
if (pick.key === 'quit') return
|
|
279
279
|
if (pick.key === 'serve') { if (await ensureLoggedIn()) await actions.serveAccountForeground() }
|
|
280
280
|
else if (pick.key === 'service') { if (await ensureLoggedIn()) { await actions.installService({ room: null }); resync() } }
|
|
281
|
-
else if (pick.key === 'restart') {
|
|
281
|
+
else if (pick.key === 'restart') {
|
|
282
|
+
const updated = await actions.restartUpdateService({ room: null })
|
|
283
|
+
// The launcher process cannot update its own in-memory VERSION. Returning to
|
|
284
|
+
// the menu after a successful service update therefore showed the old launcher
|
|
285
|
+
// version beside the new managed-service version and ended on a misleading
|
|
286
|
+
// "press Enter" pause. The service is already independently running, so close
|
|
287
|
+
// this stale installer process once the OS-level update is confirmed.
|
|
288
|
+
if (updated === true) return
|
|
289
|
+
resync()
|
|
290
|
+
}
|
|
282
291
|
else if (pick.key === 'uninstall') { await actions.uninstallService({ room: null }); resync() }
|
|
283
292
|
else if (pick.key === 'settings') await settingsMenu()
|
|
284
293
|
await io.ask('\n ' + C.dim('press Enter to return to the menu…'))
|