Files
SynthesisAgent.OpenclawPlugin/core/session-mapping.ts
zhi 38ac6d20b7 chore: initial scaffolding for SynthesisAgent.OpenclawPlugin
OpenClaw plugin that manages long-lived interactive Claude Code processes
per OpenClaw session. Process manager + session-mapping persistence +
bridge WebSocket server skeleton.

Will be rewritten to follow the contractor-agent HTTP model-provider pattern
(register as `synthesis-claude-bridge` provider, receive /v1/chat/completions,
dispatch to channel notification on the bound Claude process). See parent
repo's STATUS.md for the punch list.
2026-05-14 09:41:18 +00:00

84 lines
2.1 KiB
TypeScript

import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'
import { dirname } from 'node:path'
import { randomUUID } from 'node:crypto'
export interface SessionRecord {
openclawSessionId: string
claudeSessionUuid: string
createdAt: number
lastActiveAt: number
}
/**
* Persistent openclaw_session ↔ claude_session mapping.
*
* Single-writer (the OpenclawPlugin process). File-level on every mutation —
* simple and correct for the volume we expect. If it ever matters, swap for
* sqlite.
*/
export class SessionMapping {
private records = new Map<string, SessionRecord>()
private path: string
constructor(path: string) {
this.path = path
this.load()
}
private load(): void {
try {
const raw = readFileSync(this.path, 'utf8')
const parsed = JSON.parse(raw) as { records?: SessionRecord[] }
for (const r of parsed.records ?? []) {
this.records.set(r.openclawSessionId, r)
}
} catch {
// File doesn't exist or unreadable — start fresh.
}
}
private flush(): void {
mkdirSync(dirname(this.path), { recursive: true })
writeFileSync(
this.path,
JSON.stringify({ records: [...this.records.values()] }, null, 2),
'utf8',
)
}
get(openclawSessionId: string): SessionRecord | undefined {
return this.records.get(openclawSessionId)
}
/** Get-or-create. Returns the (possibly freshly assigned) claude_session_uuid. */
ensure(openclawSessionId: string): SessionRecord {
const existing = this.records.get(openclawSessionId)
if (existing) return existing
const now = Date.now()
const rec: SessionRecord = {
openclawSessionId,
claudeSessionUuid: randomUUID(),
createdAt: now,
lastActiveAt: now,
}
this.records.set(openclawSessionId, rec)
this.flush()
return rec
}
touch(openclawSessionId: string): void {
const r = this.records.get(openclawSessionId)
if (!r) return
r.lastActiveAt = Date.now()
this.flush()
}
forget(openclawSessionId: string): void {
if (this.records.delete(openclawSessionId)) this.flush()
}
all(): SessionRecord[] {
return [...this.records.values()]
}
}