cache per-instance history via SSE

This commit is contained in:
Shantur Rathore
2025-11-19 17:48:07 +00:00
parent 885059b0aa
commit 45dca7a7f0
6 changed files with 155 additions and 28 deletions

View File

@@ -154,6 +154,7 @@ export type WorkspaceEventType =
| "workspace.log"
| "config.appChanged"
| "config.binariesChanged"
| "instance.dataChanged"
export type WorkspaceEventPayload =
| { type: "workspace.created"; workspace: WorkspaceDescriptor }
@@ -163,6 +164,7 @@ export type WorkspaceEventPayload =
| { type: "workspace.log"; entry: WorkspaceLogEntry }
| { type: "config.appChanged"; config: AppConfig }
| { type: "config.binariesChanged"; binaries: BinaryRecord[] }
| { type: "instance.dataChanged"; instanceId: string; data: InstanceData }
export interface ServerMeta {
/** Base URL clients should target for REST calls (useful for Electron embedding). */

View File

@@ -21,6 +21,7 @@ export class EventBus extends EventEmitter {
this.on("workspace.log", handler)
this.on("config.appChanged", handler)
this.on("config.binariesChanged", handler)
this.on("instance.dataChanged", handler)
return () => {
this.off("workspace.created", handler)
this.off("workspace.started", handler)
@@ -29,6 +30,7 @@ export class EventBus extends EventEmitter {
this.off("workspace.log", handler)
this.off("config.appChanged", handler)
this.off("config.binariesChanged", handler)
this.off("instance.dataChanged", handler)
}
}
}

View File

@@ -67,9 +67,10 @@ export function createHttpServer(deps: HttpServerDeps) {
registerFilesystemRoutes(app, { fileSystemBrowser: deps.fileSystemBrowser })
registerMetaRoutes(app, { serverMeta: deps.serverMeta })
registerEventRoutes(app, { eventBus: deps.eventBus, registerClient: registerSseClient })
registerStorageRoutes(app, { instanceStore: deps.instanceStore })
registerStorageRoutes(app, { instanceStore: deps.instanceStore, eventBus: deps.eventBus })
registerInstanceProxyRoutes(app, { workspaceManager: deps.workspaceManager, logger: proxyLogger })
if (deps.uiDevServerUrl) {
setupDevProxy(app, deps.uiDevServerUrl)
} else {

View File

@@ -1,15 +1,22 @@
import { FastifyInstance } from "fastify"
import { z } from "zod"
import { InstanceStore } from "../../storage/instance-store"
import { EventBus } from "../../events/bus"
import type { InstanceData } from "../../api-types"
interface RouteDeps {
instanceStore: InstanceStore
eventBus: EventBus
}
const InstanceDataSchema = z.object({
messageHistory: z.array(z.string()).default([]),
})
const EMPTY_INSTANCE_DATA: InstanceData = {
messageHistory: [],
}
export function registerStorageRoutes(app: FastifyInstance, deps: RouteDeps) {
app.get<{ Params: { id: string } }>("/api/storage/instances/:id", async (request, reply) => {
try {
@@ -25,6 +32,7 @@ export function registerStorageRoutes(app: FastifyInstance, deps: RouteDeps) {
try {
const body = InstanceDataSchema.parse(request.body ?? {})
await deps.instanceStore.write(request.params.id, body)
deps.eventBus.publish({ type: "instance.dataChanged", instanceId: request.params.id, data: body })
reply.code(204)
} catch (error) {
reply.code(400)
@@ -35,6 +43,7 @@ export function registerStorageRoutes(app: FastifyInstance, deps: RouteDeps) {
app.delete<{ Params: { id: string } }>("/api/storage/instances/:id", async (request, reply) => {
try {
await deps.instanceStore.delete(request.params.id)
deps.eventBus.publish({ type: "instance.dataChanged", instanceId: request.params.id, data: EMPTY_INSTANCE_DATA })
reply.code(204)
} catch (error) {
reply.code(500)