/**
* debug-extractors.mjs — Call graph.compute with debug logging for each algo
* Tests what the daemon actually returns for each extractor type
*/
import Pipe from 'bare-pipe'
import FramedStream from 'framed-stream'
import b4a from 'b4a'
let rpcId = 0
const pipe = new Pipe('/tmp/keet-daemon.sock')
const framed = new FramedStream(pipe)
const pending = new Map()
framed.on('data', buf => {
const msg = JSON.parse(b4a.toString(buf))
const cb = pending.get(msg.id)
if (cb) { pending.delete(msg.id); cb(msg) }
})
function call (method, params = {}) {
return new Promise((resolve, reject) => {
const id = ++rpcId
const timeout = setTimeout(() => { pending.delete(id); reject(new Error('timeout: ' + method)) }, 30000)
pending.set(id, msg => {
clearTimeout(timeout)
if (msg.ok !== undefined && msg.data !== undefined) msg.result = msg.data
if (msg.error) reject(new Error(msg.error.message || String(msg.error)))
else resolve(msg.result)
})
framed.write(b4a.from(JSON.stringify({ jsonrpc: '2.0', id, method, params })))
})
}
// Get rooms first
const rooms = await call('rooms')
console.log(`Total rooms: ${rooms.length}\n`)
// Test each room for the raw data that extractors need
console.log('=== RAW MEMBER DATA (what creator/size extractors see) ===\n')
for (const r of rooms.slice(0, 5)) {
const rid = r.roomId || r.id
const title = r.title || '(untitled)'
try {
const members = await call('members', { roomId: rid })
const list = Array.isArray(members) ? members : []
const admin = list.find(m => m.role === 'admin')
const adminRaw = list.find(m => m.role === 1)
console.log(`${title}: ${list.length} members, admin(string)=${admin?.memberId?.slice(0,12)||'none'}, admin(num)=${adminRaw?.memberId?.slice(0,12)||'none'}`)
} catch (err) {
console.log(`${title}: ERROR — ${err.message}`)
}
}
console.log('\n=== RAW MESSAGE DATA (what activity extractor sees) ===\n')
for (const r of rooms.slice(0, 5)) {
const rid = r.roomId || r.id
const title = r.title || '(untitled)'
// Try 'read' instead of 'messages'
try {
const msgs = await call('read', { roomId: rid, limit: 1 })
const list = Array.isArray(msgs) ? msgs : (msgs?.messages || msgs?.data || [])
if (list.length > 0) {
const m = list[0]
console.log(`${title}: ${list.length} msg, ts=${m.timestamp||m.ts||'none'}, keys=${Object.keys(m).slice(0,8).join(',')}`)
} else {
console.log(`${title}: 0 messages`)
}
} catch (err) {
console.log(`${title}: ERROR — ${err.message}`)
}
}
// Now test graph.compute with each algo, checking full result
console.log('\n=== GRAPH.COMPUTE RESULTS ===\n')
const algos = [
{name: 'creator', features: [{type: 'creator', weight: 1.0}], threshold: 0.1, minSize: 2},
{name: 'activity', features: [{type: 'activity', weight: 1.0}], threshold: 0.1, minSize: 2},
{name: 'size', features: [{type: 'size', weight: 1.0}], threshold: 0.1, minSize: 2},
]
for (const algo of algos) {
try {
const result = await call('graph.compute', { force: true, algo })
const c = result.clusters || []
const e = result.edges || []
console.log(`${algo.name}: ${c.length} clusters, ${e.length} edges`)
if (c.length > 0) {
for (const cl of c) {
console.log(` cluster: label="${cl.label}", rooms=${(cl.rooms||[]).length}`)
}
}
if (e.length > 0) {
console.log(` top edge: ${JSON.stringify(e[0])}`)
}
} catch (err) {
console.log(`${algo.name}: ERROR — ${err.message}`)
}
}
pipe.destroy()
setTimeout(() => Bare.exit(0), 500)