- sabia-core: descoberta de interfaces, controle de modo monitor/canal via iw (ponte até ter nl80211 nativo), captura crua 802.11 via AF_PACKET (sem libpcap), parsing de radiotap e quadros de gerenciamento (beacon/probe/EAPOL), agregação em tabela de APs/ clientes, escrita de .pcap, trilha de auditoria encadeada por hash, auto-atualização via GitHub releases (curl, sem dependências de rede extras) - sabia-cli: `sabia iface`, `sabia scan`, `sabia update`, `sabia audit-verify`, `sabia ui` (servidor local + navegador, protegido por token, mesma filosofia do Guará) - OpenWolf inicializado para memória de projeto entre agentes
93 lines
3.8 KiB
TypeScript
93 lines
3.8 KiB
TypeScript
import * as fs from "node:fs"
|
|
import * as path from "node:path"
|
|
import { getWolfDir, writeJSON, readJSON, appendMarkdown, timeShort, timestamp, readMarkdown, sessionFilePath, gcSessionFiles } from "./fs.js"
|
|
import type { SessionState } from "./types.js"
|
|
|
|
const sessions = new Map<string, SessionState>()
|
|
|
|
export function getSessionState(sessionId: string): SessionState | undefined {
|
|
return sessions.get(sessionId)
|
|
}
|
|
|
|
export function setSessionState(sessionId: string, state: SessionState): void {
|
|
sessions.set(sessionId, state)
|
|
}
|
|
|
|
export function deleteSession(sessionId: string): void {
|
|
sessions.delete(sessionId)
|
|
}
|
|
|
|
export function handleSessionStart(directory: string, sessionId: string): void {
|
|
const wolfDir = getWolfDir(directory)
|
|
if (!fs.existsSync(wolfDir)) return
|
|
|
|
const hooksDir = path.join(wolfDir, "hooks")
|
|
fs.mkdirSync(hooksDir, { recursive: true })
|
|
|
|
gcSessionFiles(hooksDir)
|
|
|
|
const sessionFile = sessionFilePath(hooksDir, sessionId)
|
|
const existing = readJSON<SessionState | null>(sessionFile, null)
|
|
if (existing?.session_id === sessionId) { sessions.set(sessionId, existing); return }
|
|
const state: SessionState = {
|
|
session_id: sessionId,
|
|
started: timestamp(),
|
|
files_read: {},
|
|
files_written: [],
|
|
edit_counts: {},
|
|
anatomy_hits: 0,
|
|
anatomy_misses: 0,
|
|
repeated_reads_warned: 0,
|
|
cerebrum_warnings: 0,
|
|
stop_count: 0,
|
|
}
|
|
sessions.set(sessionId, state)
|
|
writeJSON(sessionFile, state)
|
|
|
|
const memoryPath = path.join(wolfDir, "memory.md")
|
|
const now = new Date()
|
|
const header = `\n## Session: ${now.toISOString().slice(0, 10)} ${timeShort()}\n\n| Time | Action | File(s) | Outcome | ~Tokens |\n|------|--------|---------|---------|--------|\n`
|
|
appendMarkdown(memoryPath, header)
|
|
|
|
try {
|
|
const cerebrumPath = path.join(wolfDir, "cerebrum.md")
|
|
const cerebrumContent = fs.readFileSync(cerebrumPath, "utf-8")
|
|
const stat = fs.statSync(cerebrumPath)
|
|
const daysSinceUpdate = (Date.now() - stat.mtimeMs) / (1000 * 60 * 60 * 24)
|
|
const entryLines = cerebrumContent.split("\n").filter(l => {
|
|
const t = l.trim()
|
|
return t.startsWith("- ") || t.startsWith("* ") || (t.startsWith("[") && t.includes("]"))
|
|
})
|
|
if (entryLines.length < 3) {
|
|
console.warn(`💡 OpenWolf: cerebrum.md has only ${entryLines.length} entries. Learn from this session — record user preferences, project conventions, and mistakes to .wolf/cerebrum.md.`)
|
|
} else if (daysSinceUpdate > 3) {
|
|
console.warn(`💡 OpenWolf: cerebrum.md hasn't been updated in ${Math.floor(daysSinceUpdate)} days. Look for opportunities to add learnings this session.`)
|
|
}
|
|
} catch {}
|
|
|
|
try {
|
|
const buglogPath = path.join(wolfDir, "buglog.json")
|
|
const buglog = readJSON<{ bugs: unknown[] }>(buglogPath, { bugs: [] })
|
|
if (buglog.bugs.length === 0) {
|
|
console.warn(`📋 OpenWolf: buglog.json is empty. If you encounter or fix any bugs, errors, or failed tests this session, log them to .wolf/buglog.json.`)
|
|
}
|
|
} catch {}
|
|
|
|
// Count the new session in the lifetime totals. readJSON deep-merges the
|
|
// fallback, so a pre-2.0 ledger without `lifetime` still gets the default;
|
|
// the guard below covers a ledger where `lifetime` is not an object at all.
|
|
const ledgerPath = path.join(wolfDir, "token-ledger.json")
|
|
const ledger = readJSON<Record<string, unknown>>(ledgerPath, { version: 1, lifetime: { total_sessions: 0 } }) as {
|
|
version: number
|
|
lifetime: { total_sessions: number }
|
|
[key: string]: unknown
|
|
}
|
|
if (!ledger.lifetime || typeof ledger.lifetime !== "object") {
|
|
ledger.lifetime = { total_sessions: 0 }
|
|
}
|
|
if (typeof ledger.lifetime.total_sessions !== "number" || !isFinite(ledger.lifetime.total_sessions)) {
|
|
ledger.lifetime.total_sessions = 0
|
|
}
|
|
ledger.lifetime.total_sessions++
|
|
writeJSON(ledgerPath, ledger)
|
|
} |