Files
claude-code/packages/remote-control-server/src/routes/v2/worker-events.ts
2026-05-01 21:39:30 +08:00

145 lines
3.8 KiB
TypeScript

import { Hono } from 'hono'
import { sessionIngressAuth, acceptCliHeaders } from '../../auth/middleware'
import { publishSessionEvent } from '../../services/transport'
import {
getSession,
touchSession,
updateSessionStatus,
} from '../../services/session'
const app = new Hono()
function extractWorkerEvents(body: unknown): Array<Record<string, unknown>> {
if (!body || typeof body !== 'object') {
return []
}
const payload = body as Record<string, unknown>
const rawEvents = Array.isArray(payload.events)
? payload.events
: Array.isArray(body)
? body
: [body]
return rawEvents
.filter(
(evt): evt is Record<string, unknown> => !!evt && typeof evt === 'object',
)
.map(evt => {
const wrappedPayload = evt.payload
if (
wrappedPayload &&
typeof wrappedPayload === 'object' &&
!Array.isArray(wrappedPayload)
) {
return wrappedPayload as Record<string, unknown>
}
return evt
})
}
/** POST /v1/code/sessions/:id/worker/events — Write events */
app.post(
'/:id/worker/events',
acceptCliHeaders,
sessionIngressAuth,
async c => {
const sessionId = c.req.param('id')!
if (!getSession(sessionId)) {
return c.json(
{ error: { type: 'not_found', message: 'Session not found' } },
404,
)
}
const body = await c.req.json()
const events = extractWorkerEvents(body)
const published = []
for (const evt of events) {
const eventType = typeof evt.type === 'string' ? evt.type : 'message'
const result = publishSessionEvent(sessionId, eventType, evt, 'inbound')
published.push(result)
}
touchSession(sessionId)
return c.json({ status: 'ok', count: published.length }, 200)
},
)
/** PUT /v1/code/sessions/:id/worker/state — Report worker state */
app.put('/:id/worker/state', acceptCliHeaders, sessionIngressAuth, async c => {
const sessionId = c.req.param('id')!
if (!getSession(sessionId)) {
return c.json(
{ error: { type: 'not_found', message: 'Session not found' } },
404,
)
}
const body = await c.req.json()
if (body.status) {
updateSessionStatus(sessionId, body.status)
} else {
touchSession(sessionId)
}
return c.json({ status: 'ok' }, 200)
})
/** PUT /v1/code/sessions/:id/worker/external_metadata — Report worker metadata (no-op) */
app.put(
'/:id/worker/external_metadata',
acceptCliHeaders,
sessionIngressAuth,
async c => {
const sessionId = c.req.param('id')!
if (!getSession(sessionId)) {
return c.json(
{ error: { type: 'not_found', message: 'Session not found' } },
404,
)
}
// TUI's CCRClient calls this for metadata reporting. Accept and discard.
return c.json({ status: 'ok' }, 200)
},
)
/** POST /v1/code/sessions/:id/worker/events/delivery — Batch delivery tracking (no-op) */
app.post(
'/:id/worker/events/delivery',
acceptCliHeaders,
sessionIngressAuth,
async c => {
const sessionId = c.req.param('id')!
if (!getSession(sessionId)) {
return c.json(
{ error: { type: 'not_found', message: 'Session not found' } },
404,
)
}
return c.json({ status: 'ok' }, 200)
},
)
/** POST /v1/code/sessions/:id/worker/events/:eventId/delivery — Delivery tracking (no-op) */
app.post(
'/:id/worker/events/:eventId/delivery',
acceptCliHeaders,
sessionIngressAuth,
async c => {
const sessionId = c.req.param('id')!
if (!getSession(sessionId)) {
return c.json(
{ error: { type: 'not_found', message: 'Session not found' } },
404,
)
}
// TUI's CCRClient reports event delivery status (received/processing/processed).
// Accept and discard — event bus doesn't track per-event delivery.
return c.json({ status: 'ok' }, 200)
},
)
export default app