refactor: remove session expiry, add syncWithOpenCode

- Remove expireInactive and startExpiryLoop
- Add syncWithOpenCode() to prune orphaned sessions
- Add logging for session map file path and load count
This commit is contained in:
Бородин Роман 2026-09-25 15:44:33 +03:00
parent 01bf23543a
commit 7db5bc6989
2 changed files with 23 additions and 27 deletions

View File

@ -36,8 +36,6 @@ async function runPlugin(ctx: any, options: any, sdkClient?: any) {
log(`Configuration loaded`) log(`Configuration loaded`)
const sessionRetentionMs = 30 * 60 * 1000
const matrix = new MatrixBotClient(options) const matrix = new MatrixBotClient(options)
const sessionManager = new SessionManager(options.rateLimitSeconds || 5) const sessionManager = new SessionManager(options.rateLimitSeconds || 5)
@ -289,10 +287,7 @@ async function runPlugin(ctx: any, options: any, sdkClient?: any) {
} }
}) })
// Session expiry loop // Start Matrix client
expiryInterval = sessionManager.startExpiryLoop(sessionRetentionMs)
// V2 Storage - defensive access
if (isV2 && ctx?.storage) { if (isV2 && ctx?.storage) {
await ctx.storage.set("matrix/plugin_version", "1.0.0") await ctx.storage.set("matrix/plugin_version", "1.0.0")
await ctx.storage.set("matrix/started_at", new Date().toISOString()) await ctx.storage.set("matrix/started_at", new Date().toISOString())
@ -306,7 +301,6 @@ async function runPlugin(ctx: any, options: any, sdkClient?: any) {
cleanup = () => { cleanup = () => {
log("Plugin cleaning up...") log("Plugin cleaning up...")
eventController.abort() eventController.abort()
if (expiryInterval) clearInterval(expiryInterval)
matrix.stop() matrix.stop()
} }

View File

@ -21,6 +21,7 @@ export function encodeSessionId(matrixRoomId: string, threadRootId: string): str
} }
const SESSION_MAP_FILE = path.join(process.env.HOME || "/tmp", ".opencode-matrix-sessions.json") const SESSION_MAP_FILE = path.join(process.env.HOME || "/tmp", ".opencode-matrix-sessions.json")
console.log(`[SessionManager] SESSION_MAP_FILE=${SESSION_MAP_FILE}`)
interface SessionMapEntry { interface SessionMapEntry {
matrixRoomId: string matrixRoomId: string
@ -37,15 +38,18 @@ function loadSessionMap(): Map<string, SessionMapEntry> {
try { try {
if (fs.existsSync(SESSION_MAP_FILE)) { if (fs.existsSync(SESSION_MAP_FILE)) {
const data = JSON.parse(fs.readFileSync(SESSION_MAP_FILE, "utf-8")) const data = JSON.parse(fs.readFileSync(SESSION_MAP_FILE, "utf-8"))
console.log(`[SessionManager] Loaded ${data.length} sessions from ${SESSION_MAP_FILE}`)
if (Array.isArray(data)) { if (Array.isArray(data)) {
for (const entry of data) { for (const entry of data) {
const key = `${entry.matrixRoomId}::${entry.threadRootId}` const key = `${entry.matrixRoomId}::${entry.threadRootId}`
result.set(key, entry) result.set(key, entry)
} }
} }
} else {
console.log(`[SessionManager] File not found: ${SESSION_MAP_FILE}`)
} }
} catch (e) { } catch (e) {
// ignore console.log(`[SessionManager] Error loading: ${e}`)
} }
return result return result
} }
@ -166,25 +170,6 @@ export class SessionManager {
return false return false
} }
expireInactive(maxAgeMs: number): void {
const now = Date.now()
for (const [sessionId, session] of this.sessions) {
if (now - session.lastActivity > maxAgeMs) {
const key = sessionId
this.sessions.delete(key)
this.persistentMap.delete(key)
this.activeQueries.delete(key)
}
}
saveSessionMap(this.persistentMap)
}
startExpiryLoop(maxAgeMs: number, intervalMs: number = 60000): NodeJS.Timeout {
return setInterval(() => {
this.expireInactive(maxAgeMs)
}, intervalMs)
}
saveSession(matrixRoomId: string, threadRootId: string, opencodeSessionId: string): void { saveSession(matrixRoomId: string, threadRootId: string, opencodeSessionId: string): void {
const key = this.getSessionKey(matrixRoomId, threadRootId) const key = this.getSessionKey(matrixRoomId, threadRootId)
const session = this.sessions.get(key) const session = this.sessions.get(key)
@ -202,4 +187,21 @@ export class SessionManager {
saveSessionMap(this.persistentMap) saveSessionMap(this.persistentMap)
} }
} }
syncWithOpenCode(openCodeSessionIds: Set<string>): void {
const toDelete: string[] = []
for (const [, entry] of this.persistentMap) {
if (!openCodeSessionIds.has(entry.opencodeSessionId)) {
toDelete.push(`${entry.matrixRoomId}::${entry.threadRootId}`)
}
}
for (const key of toDelete) {
this.persistentMap.delete(key)
this.sessions.delete(key)
this.activeQueries.delete(key)
}
if (toDelete.length > 0) {
saveSessionMap(this.persistentMap)
}
}
} }