buzz listener: verified receipt lifecycle (👀+💬 on receipt, removed on reply)
This commit is contained in:
parent
028175b612
commit
d47dbff7f3
1 changed files with 16 additions and 1 deletions
|
|
@ -16,7 +16,9 @@ const ME = '4abe5fcdf9695be34bdfc8fe82297aaaf0cf65b5ca92b6c789f6f8783b5ee197'
|
||||||
const INBOX = '/home/opc/.buzz-inbox'
|
const INBOX = '/home/opc/.buzz-inbox'
|
||||||
const STATE = '/home/opc/.buzz-last-seen'
|
const STATE = '/home/opc/.buzz-last-seen'
|
||||||
const PENDING = '/home/opc/.buzz-pending.json'
|
const PENDING = '/home/opc/.buzz-pending.json'
|
||||||
const MAX_LIFETIME = 120 * 1000 // reconnect every 2 min: replays since lastSeen (zombie-socket proof)
|
const MAX_LIFETIME = 6 * 3600 * 1000 // periodic reconnect for hygiene
|
||||||
|
const RESUBSCRIBE_MS = 30 * 1000 // liveness probe: re-REQ pulls missed events + EOSE proves the socket
|
||||||
|
const DEAD_AFTER_MS = 60 * 1000 // no frames for this long -> socket assumed dead, exit for restart
|
||||||
|
|
||||||
const keyText = fs.readFileSync('/home/opc/buzz-agent-key.txt', 'utf8')
|
const keyText = fs.readFileSync('/home/opc/buzz-agent-key.txt', 'utf8')
|
||||||
const SK = Uint8Array.from(keyText.match(/SECRET:\s*(\S+)/)[1].match(/.{2}/g).map((h) => parseInt(h, 16)))
|
const SK = Uint8Array.from(keyText.match(/SECRET:\s*(\S+)/)[1].match(/.{2}/g).map((h) => parseInt(h, 16)))
|
||||||
|
|
@ -109,6 +111,10 @@ function subscribe() {
|
||||||
ws.send(JSON.stringify(['REQ', 'workspace', { kinds: [9], since: lastSeen() }]))
|
ws.send(JSON.stringify(['REQ', 'workspace', { kinds: [9], since: lastSeen() }]))
|
||||||
console.error('subscribed to all kind-9 messages since', lastSeen())
|
console.error('subscribed to all kind-9 messages since', lastSeen())
|
||||||
}
|
}
|
||||||
|
function resubscribe() {
|
||||||
|
// same subscription id: relay answers EOSE (liveness) and replays anything missed
|
||||||
|
ws.send(JSON.stringify(['REQ', 'workspace', { kinds: [9], since: lastSeen() }]))
|
||||||
|
}
|
||||||
|
|
||||||
function sendAuth(challenge) {
|
function sendAuth(challenge) {
|
||||||
const ev = finalizeEvent(
|
const ev = finalizeEvent(
|
||||||
|
|
@ -124,6 +130,7 @@ ws.onopen = () => {
|
||||||
console.error('ws open — waiting for auth challenge')
|
console.error('ws open — waiting for auth challenge')
|
||||||
}
|
}
|
||||||
ws.onmessage = (msg) => {
|
ws.onmessage = (msg) => {
|
||||||
|
lastFrameAt = Date.now()
|
||||||
let data
|
let data
|
||||||
try { data = JSON.parse(msg.data) } catch { return }
|
try { data = JSON.parse(msg.data) } catch { return }
|
||||||
const [type, ...rest] = data
|
const [type, ...rest] = data
|
||||||
|
|
@ -167,6 +174,14 @@ ws.onmessage = (msg) => {
|
||||||
ws.onclose = () => { console.error('ws closed'); process.exit(2) }
|
ws.onclose = () => { console.error('ws closed'); process.exit(2) }
|
||||||
ws.onerror = () => {}
|
ws.onerror = () => {}
|
||||||
|
|
||||||
|
setInterval(resubscribe, RESUBSCRIBE_MS)
|
||||||
|
setInterval(() => {
|
||||||
|
if (Date.now() - lastFrameAt > DEAD_AFTER_MS) {
|
||||||
|
console.error('socket dead — no frames for 60s')
|
||||||
|
process.exit(2)
|
||||||
|
}
|
||||||
|
}, 15000)
|
||||||
|
let lastFrameAt = Date.now()
|
||||||
setTimeout(() => { console.error('lifetime elapsed'); process.exit(3) }, MAX_LIFETIME)
|
setTimeout(() => { console.error('lifetime elapsed'); process.exit(3) }, MAX_LIFETIME)
|
||||||
|
|
||||||
const BUZZ_CLI = '/usr/local/bin/buzz'
|
const BUZZ_CLI = '/usr/local/bin/buzz'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue