mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-22 08:15:53 +00:00
style: 完成所有文件的lint
This commit is contained in:
@@ -1,27 +1,30 @@
|
||||
import { Hono } from "hono";
|
||||
import { storeBindSession } from "../../store";
|
||||
import { resolveExistingWebSessionId, toWebSessionId } from "../../services/session";
|
||||
import { Hono } from 'hono'
|
||||
import { storeBindSession } from '../../store'
|
||||
import {
|
||||
resolveExistingWebSessionId,
|
||||
toWebSessionId,
|
||||
} from '../../services/session'
|
||||
|
||||
const app = new Hono();
|
||||
const app = new Hono()
|
||||
|
||||
/** POST /web/bind — Bind a session to a UUID (no-login auth) */
|
||||
app.post("/bind", async (c) => {
|
||||
const body = await c.req.json();
|
||||
const sessionId = body.sessionId;
|
||||
app.post('/bind', async c => {
|
||||
const body = await c.req.json()
|
||||
const sessionId = body.sessionId
|
||||
// UUID can come from query param (api.js sends it in URL) or body
|
||||
const uuid = c.req.query("uuid") || body.uuid;
|
||||
const uuid = c.req.query('uuid') || body.uuid
|
||||
|
||||
if (!sessionId || !uuid) {
|
||||
return c.json({ error: "sessionId and uuid are required" }, 400);
|
||||
return c.json({ error: 'sessionId and uuid are required' }, 400)
|
||||
}
|
||||
|
||||
const resolvedSessionId = resolveExistingWebSessionId(sessionId);
|
||||
const resolvedSessionId = resolveExistingWebSessionId(sessionId)
|
||||
if (!resolvedSessionId) {
|
||||
return c.json({ error: "Session not found" }, 404);
|
||||
return c.json({ error: 'Session not found' }, 404)
|
||||
}
|
||||
|
||||
storeBindSession(resolvedSessionId, uuid);
|
||||
return c.json({ ok: true, sessionId: toWebSessionId(resolvedSessionId) });
|
||||
});
|
||||
storeBindSession(resolvedSessionId, uuid)
|
||||
return c.json({ ok: true, sessionId: toWebSessionId(resolvedSessionId) })
|
||||
})
|
||||
|
||||
export default app;
|
||||
export default app
|
||||
|
||||
@@ -1,86 +1,130 @@
|
||||
import { log, error as logError } from "../../logger";
|
||||
import { Hono } from "hono";
|
||||
import { uuidAuth } from "../../auth/middleware";
|
||||
import { getSession, isSessionClosedStatus, resolveOwnedWebSessionId, updateSessionStatus } from "../../services/session";
|
||||
import { publishSessionEvent } from "../../services/transport";
|
||||
import { getEventBus } from "../../transport/event-bus";
|
||||
import { log, error as logError } from '../../logger'
|
||||
import { Hono } from 'hono'
|
||||
import { uuidAuth } from '../../auth/middleware'
|
||||
import {
|
||||
getSession,
|
||||
isSessionClosedStatus,
|
||||
resolveOwnedWebSessionId,
|
||||
updateSessionStatus,
|
||||
} from '../../services/session'
|
||||
import { publishSessionEvent } from '../../services/transport'
|
||||
import { getEventBus } from '../../transport/event-bus'
|
||||
|
||||
const app = new Hono();
|
||||
const app = new Hono()
|
||||
|
||||
type OwnershipCheckResult =
|
||||
| { error: true }
|
||||
| { error: true; reason: string }
|
||||
| { error: false; session: NonNullable<ReturnType<typeof getSession>>; sessionId: string };
|
||||
| {
|
||||
error: false
|
||||
session: NonNullable<ReturnType<typeof getSession>>
|
||||
sessionId: string
|
||||
}
|
||||
|
||||
function checkOwnership(c: { get: (key: string) => string | undefined }, sessionId: string): OwnershipCheckResult {
|
||||
const uuid = c.get("uuid")!;
|
||||
const resolvedSessionId = resolveOwnedWebSessionId(sessionId, uuid);
|
||||
function checkOwnership(
|
||||
c: { get: (key: string) => string | undefined },
|
||||
sessionId: string,
|
||||
): OwnershipCheckResult {
|
||||
const uuid = c.get('uuid')!
|
||||
const resolvedSessionId = resolveOwnedWebSessionId(sessionId, uuid)
|
||||
if (!resolvedSessionId) {
|
||||
return { error: true };
|
||||
return { error: true }
|
||||
}
|
||||
const session = getSession(resolvedSessionId);
|
||||
const session = getSession(resolvedSessionId)
|
||||
if (!session) {
|
||||
return { error: true };
|
||||
return { error: true }
|
||||
}
|
||||
if (isSessionClosedStatus(session.status)) {
|
||||
return { error: true, reason: `Session is ${session.status}` };
|
||||
return { error: true, reason: `Session is ${session.status}` }
|
||||
}
|
||||
return { error: false, session, sessionId: resolvedSessionId };
|
||||
return { error: false, session, sessionId: resolvedSessionId }
|
||||
}
|
||||
|
||||
function closedSessionResponse(message: string) {
|
||||
return { error: { type: "session_closed", message } };
|
||||
return { error: { type: 'session_closed', message } }
|
||||
}
|
||||
|
||||
/** POST /web/sessions/:id/events — Send user message to session */
|
||||
app.post("/sessions/:id/events", uuidAuth, async (c) => {
|
||||
const requestedSessionId = c.req.param("id")!;
|
||||
const ownership = checkOwnership(c, requestedSessionId);
|
||||
app.post('/sessions/:id/events', uuidAuth, async c => {
|
||||
const requestedSessionId = c.req.param('id')!
|
||||
const ownership = checkOwnership(c, requestedSessionId)
|
||||
if (ownership.error) {
|
||||
const message = "reason" in ownership ? ownership.reason : "Not your session";
|
||||
const status = "reason" in ownership ? 409 : 403;
|
||||
return c.json("reason" in ownership ? closedSessionResponse(message) : { error: { type: "forbidden", message } }, status);
|
||||
const message =
|
||||
'reason' in ownership ? ownership.reason : 'Not your session'
|
||||
const status = 'reason' in ownership ? 409 : 403
|
||||
return c.json(
|
||||
'reason' in ownership
|
||||
? closedSessionResponse(message)
|
||||
: { error: { type: 'forbidden', message } },
|
||||
status,
|
||||
)
|
||||
}
|
||||
const { sessionId } = ownership;
|
||||
const { sessionId } = ownership
|
||||
|
||||
const body = await c.req.json();
|
||||
const eventType = body.type || "user";
|
||||
log(`[RC-DEBUG] web -> server: POST /web/sessions/${sessionId}/events type=${eventType} content=${JSON.stringify(body).slice(0, 200)}`);
|
||||
const event = publishSessionEvent(sessionId, eventType, body, "outbound");
|
||||
log(`[RC-DEBUG] web -> server: published outbound event id=${event.id} type=${event.type} direction=${event.direction} subscribers=${getEventBus(sessionId).subscriberCount()}`);
|
||||
return c.json({ status: "ok", event }, 200);
|
||||
});
|
||||
const body = await c.req.json()
|
||||
const eventType = body.type || 'user'
|
||||
log(
|
||||
`[RC-DEBUG] web -> server: POST /web/sessions/${sessionId}/events type=${eventType} content=${JSON.stringify(body).slice(0, 200)}`,
|
||||
)
|
||||
const event = publishSessionEvent(sessionId, eventType, body, 'outbound')
|
||||
log(
|
||||
`[RC-DEBUG] web -> server: published outbound event id=${event.id} type=${event.type} direction=${event.direction} subscribers=${getEventBus(sessionId).subscriberCount()}`,
|
||||
)
|
||||
return c.json({ status: 'ok', event }, 200)
|
||||
})
|
||||
|
||||
/** POST /web/sessions/:id/control — Send control request (permission approval etc) */
|
||||
app.post("/sessions/:id/control", uuidAuth, async (c) => {
|
||||
const requestedSessionId = c.req.param("id")!;
|
||||
const ownership = checkOwnership(c, requestedSessionId);
|
||||
app.post('/sessions/:id/control', uuidAuth, async c => {
|
||||
const requestedSessionId = c.req.param('id')!
|
||||
const ownership = checkOwnership(c, requestedSessionId)
|
||||
if (ownership.error) {
|
||||
const message = "reason" in ownership ? ownership.reason : "Not your session";
|
||||
const status = "reason" in ownership ? 409 : 403;
|
||||
return c.json("reason" in ownership ? closedSessionResponse(message) : { error: { type: "forbidden", message } }, status);
|
||||
const message =
|
||||
'reason' in ownership ? ownership.reason : 'Not your session'
|
||||
const status = 'reason' in ownership ? 409 : 403
|
||||
return c.json(
|
||||
'reason' in ownership
|
||||
? closedSessionResponse(message)
|
||||
: { error: { type: 'forbidden', message } },
|
||||
status,
|
||||
)
|
||||
}
|
||||
const { sessionId } = ownership;
|
||||
const { sessionId } = ownership
|
||||
|
||||
const body = await c.req.json();
|
||||
const event = publishSessionEvent(sessionId, body.type || "control_request", body, "outbound");
|
||||
return c.json({ status: "ok", event }, 200);
|
||||
});
|
||||
const body = await c.req.json()
|
||||
const event = publishSessionEvent(
|
||||
sessionId,
|
||||
body.type || 'control_request',
|
||||
body,
|
||||
'outbound',
|
||||
)
|
||||
return c.json({ status: 'ok', event }, 200)
|
||||
})
|
||||
|
||||
/** POST /web/sessions/:id/interrupt — Interrupt session */
|
||||
app.post("/sessions/:id/interrupt", uuidAuth, async (c) => {
|
||||
const requestedSessionId = c.req.param("id")!;
|
||||
const ownership = checkOwnership(c, requestedSessionId);
|
||||
app.post('/sessions/:id/interrupt', uuidAuth, async c => {
|
||||
const requestedSessionId = c.req.param('id')!
|
||||
const ownership = checkOwnership(c, requestedSessionId)
|
||||
if (ownership.error) {
|
||||
const message = "reason" in ownership ? ownership.reason : "Not your session";
|
||||
const status = "reason" in ownership ? 409 : 403;
|
||||
return c.json("reason" in ownership ? closedSessionResponse(message) : { error: { type: "forbidden", message } }, status);
|
||||
const message =
|
||||
'reason' in ownership ? ownership.reason : 'Not your session'
|
||||
const status = 'reason' in ownership ? 409 : 403
|
||||
return c.json(
|
||||
'reason' in ownership
|
||||
? closedSessionResponse(message)
|
||||
: { error: { type: 'forbidden', message } },
|
||||
status,
|
||||
)
|
||||
}
|
||||
const { sessionId } = ownership;
|
||||
const { sessionId } = ownership
|
||||
|
||||
publishSessionEvent(sessionId, "interrupt", { action: "interrupt" }, "outbound");
|
||||
updateSessionStatus(sessionId, "idle");
|
||||
return c.json({ status: "ok" }, 200);
|
||||
});
|
||||
publishSessionEvent(
|
||||
sessionId,
|
||||
'interrupt',
|
||||
{ action: 'interrupt' },
|
||||
'outbound',
|
||||
)
|
||||
updateSessionStatus(sessionId, 'idle')
|
||||
return c.json({ status: 'ok' }, 200)
|
||||
})
|
||||
|
||||
export default app;
|
||||
export default app
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Hono } from "hono";
|
||||
import { uuidAuth } from "../../auth/middleware";
|
||||
import { listActiveEnvironmentsResponse } from "../../services/environment";
|
||||
import { Hono } from 'hono'
|
||||
import { uuidAuth } from '../../auth/middleware'
|
||||
import { listActiveEnvironmentsResponse } from '../../services/environment'
|
||||
|
||||
const app = new Hono();
|
||||
const app = new Hono()
|
||||
|
||||
/** GET /web/environments — List active environments (UUID-based, no user filtering) */
|
||||
app.get("/environments", uuidAuth, async (c) => {
|
||||
app.get('/environments', uuidAuth, async c => {
|
||||
// Environments are shared across all UUIDs for now
|
||||
const envs = listActiveEnvironmentsResponse();
|
||||
return c.json(envs, 200);
|
||||
});
|
||||
const envs = listActiveEnvironmentsResponse()
|
||||
return c.json(envs, 200)
|
||||
})
|
||||
|
||||
export default app;
|
||||
export default app
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { log, error as logError } from "../../logger";
|
||||
import { Hono } from "hono";
|
||||
import { uuidAuth } from "../../auth/middleware";
|
||||
import { getAutomationStateSnapshot } from "../../services/automationState";
|
||||
import { log, error as logError } from '../../logger'
|
||||
import { Hono } from 'hono'
|
||||
import { uuidAuth } from '../../auth/middleware'
|
||||
import { getAutomationStateSnapshot } from '../../services/automationState'
|
||||
import {
|
||||
createSession,
|
||||
getSession,
|
||||
@@ -10,109 +10,137 @@ import {
|
||||
listWebSessionsByOwnerUuid,
|
||||
resolveOwnedWebSessionId,
|
||||
toWebSessionResponse,
|
||||
} from "../../services/session";
|
||||
import { storeBindSession, storeGetSessionWorker } from "../../store";
|
||||
import { createWorkItem } from "../../services/work-dispatch";
|
||||
import { createSSEStream } from "../../transport/sse-writer";
|
||||
import { getEventBus } from "../../transport/event-bus";
|
||||
} from '../../services/session'
|
||||
import { storeBindSession, storeGetSessionWorker } from '../../store'
|
||||
import { createWorkItem } from '../../services/work-dispatch'
|
||||
import { createSSEStream } from '../../transport/sse-writer'
|
||||
import { getEventBus } from '../../transport/event-bus'
|
||||
|
||||
const app = new Hono();
|
||||
const app = new Hono()
|
||||
|
||||
/** POST /web/sessions — Create a session from web UI */
|
||||
app.post("/sessions", uuidAuth, async (c) => {
|
||||
const uuid = c.get("uuid")!;
|
||||
const body = await c.req.json();
|
||||
app.post('/sessions', uuidAuth, async c => {
|
||||
const uuid = c.get('uuid')!
|
||||
const body = await c.req.json()
|
||||
const session = createSession({
|
||||
environment_id: body.environment_id || null,
|
||||
title: body.title || "New Session",
|
||||
source: "web",
|
||||
permission_mode: body.permission_mode || "default",
|
||||
});
|
||||
title: body.title || 'New Session',
|
||||
source: 'web',
|
||||
permission_mode: body.permission_mode || 'default',
|
||||
})
|
||||
|
||||
// Auto-bind to creator's UUID
|
||||
storeBindSession(session.id, uuid);
|
||||
storeBindSession(session.id, uuid)
|
||||
|
||||
// Dispatch work to environment if specified
|
||||
if (body.environment_id) {
|
||||
try {
|
||||
await createWorkItem(body.environment_id, session.id);
|
||||
await createWorkItem(body.environment_id, session.id)
|
||||
} catch (err) {
|
||||
logError(`[RCS] Failed to create work item: ${(err as Error).message}`);
|
||||
logError(`[RCS] Failed to create work item: ${(err as Error).message}`)
|
||||
}
|
||||
}
|
||||
|
||||
return c.json(session, 200);
|
||||
});
|
||||
return c.json(session, 200)
|
||||
})
|
||||
|
||||
/** GET /web/sessions — List sessions owned by the requesting UUID */
|
||||
app.get("/sessions", uuidAuth, async (c) => {
|
||||
const uuid = c.get("uuid")!;
|
||||
const sessions = listWebSessionsByOwnerUuid(uuid);
|
||||
return c.json(sessions, 200);
|
||||
});
|
||||
app.get('/sessions', uuidAuth, async c => {
|
||||
const uuid = c.get('uuid')!
|
||||
const sessions = listWebSessionsByOwnerUuid(uuid)
|
||||
return c.json(sessions, 200)
|
||||
})
|
||||
|
||||
/** GET /web/sessions/all — List sessions owned by the requesting UUID (unowned sessions excluded) */
|
||||
app.get("/sessions/all", uuidAuth, async (c) => {
|
||||
const uuid = c.get("uuid")!;
|
||||
const sessions = listWebSessionSummariesByOwnerUuid(uuid);
|
||||
return c.json(sessions, 200);
|
||||
});
|
||||
app.get('/sessions/all', uuidAuth, async c => {
|
||||
const uuid = c.get('uuid')!
|
||||
const sessions = listWebSessionSummariesByOwnerUuid(uuid)
|
||||
return c.json(sessions, 200)
|
||||
})
|
||||
|
||||
/** GET /web/sessions/:id — Session detail */
|
||||
app.get("/sessions/:id", uuidAuth, async (c) => {
|
||||
const uuid = c.get("uuid")!;
|
||||
const sessionId = resolveOwnedWebSessionId(c.req.param("id")!, uuid);
|
||||
app.get('/sessions/:id', uuidAuth, async c => {
|
||||
const uuid = c.get('uuid')!
|
||||
const sessionId = resolveOwnedWebSessionId(c.req.param('id')!, uuid)
|
||||
if (!sessionId) {
|
||||
return c.json({ error: { type: "forbidden", message: "Not your session" } }, 403);
|
||||
return c.json(
|
||||
{ error: { type: 'forbidden', message: 'Not your session' } },
|
||||
403,
|
||||
)
|
||||
}
|
||||
const session = getSession(sessionId);
|
||||
const session = getSession(sessionId)
|
||||
if (!session) {
|
||||
return c.json({ error: { type: "not_found", message: "Session not found" } }, 404);
|
||||
return c.json(
|
||||
{ error: { type: 'not_found', message: 'Session not found' } },
|
||||
404,
|
||||
)
|
||||
}
|
||||
const worker = storeGetSessionWorker(sessionId);
|
||||
const automationState = getAutomationStateSnapshot(worker?.externalMetadata);
|
||||
const response = toWebSessionResponse(session);
|
||||
const worker = storeGetSessionWorker(sessionId)
|
||||
const automationState = getAutomationStateSnapshot(worker?.externalMetadata)
|
||||
const response = toWebSessionResponse(session)
|
||||
return c.json(
|
||||
automationState === undefined ? response : { ...response, automation_state: automationState },
|
||||
automationState === undefined
|
||||
? response
|
||||
: { ...response, automation_state: automationState },
|
||||
200,
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
/** GET /web/sessions/:id/history — Historical events for session */
|
||||
app.get("/sessions/:id/history", uuidAuth, async (c) => {
|
||||
const uuid = c.get("uuid")!;
|
||||
const sessionId = resolveOwnedWebSessionId(c.req.param("id")!, uuid);
|
||||
app.get('/sessions/:id/history', uuidAuth, async c => {
|
||||
const uuid = c.get('uuid')!
|
||||
const sessionId = resolveOwnedWebSessionId(c.req.param('id')!, uuid)
|
||||
if (!sessionId) {
|
||||
return c.json({ error: { type: "forbidden", message: "Not your session" } }, 403);
|
||||
return c.json(
|
||||
{ error: { type: 'forbidden', message: 'Not your session' } },
|
||||
403,
|
||||
)
|
||||
}
|
||||
const session = getSession(sessionId);
|
||||
const session = getSession(sessionId)
|
||||
if (!session) {
|
||||
return c.json({ error: { type: "not_found", message: "Session not found" } }, 404);
|
||||
return c.json(
|
||||
{ error: { type: 'not_found', message: 'Session not found' } },
|
||||
404,
|
||||
)
|
||||
}
|
||||
|
||||
const bus = getEventBus(sessionId);
|
||||
const events = bus.getEventsSince(0);
|
||||
return c.json({ events }, 200);
|
||||
});
|
||||
const bus = getEventBus(sessionId)
|
||||
const events = bus.getEventsSince(0)
|
||||
return c.json({ events }, 200)
|
||||
})
|
||||
|
||||
/** SSE /web/sessions/:id/events — Real-time event stream */
|
||||
app.get("/sessions/:id/events", uuidAuth, async (c) => {
|
||||
const uuid = c.get("uuid")!;
|
||||
const sessionId = resolveOwnedWebSessionId(c.req.param("id")!, uuid);
|
||||
app.get('/sessions/:id/events', uuidAuth, async c => {
|
||||
const uuid = c.get('uuid')!
|
||||
const sessionId = resolveOwnedWebSessionId(c.req.param('id')!, uuid)
|
||||
if (!sessionId) {
|
||||
return c.json({ error: { type: "forbidden", message: "Not your session" } }, 403);
|
||||
return c.json(
|
||||
{ error: { type: 'forbidden', message: 'Not your session' } },
|
||||
403,
|
||||
)
|
||||
}
|
||||
const session = getSession(sessionId);
|
||||
const session = getSession(sessionId)
|
||||
if (!session) {
|
||||
return c.json({ error: { type: "not_found", message: "Session not found" } }, 404);
|
||||
return c.json(
|
||||
{ error: { type: 'not_found', message: 'Session not found' } },
|
||||
404,
|
||||
)
|
||||
}
|
||||
if (isSessionClosedStatus(session.status)) {
|
||||
return c.json({ error: { type: "session_closed", message: `Session is ${session.status}` } }, 409);
|
||||
return c.json(
|
||||
{
|
||||
error: {
|
||||
type: 'session_closed',
|
||||
message: `Session is ${session.status}`,
|
||||
},
|
||||
},
|
||||
409,
|
||||
)
|
||||
}
|
||||
|
||||
const lastEventId = c.req.header("Last-Event-ID");
|
||||
const fromSeqNum = lastEventId ? parseInt(lastEventId) : 0;
|
||||
return createSSEStream(c, sessionId, fromSeqNum);
|
||||
});
|
||||
const lastEventId = c.req.header('Last-Event-ID')
|
||||
const fromSeqNum = lastEventId ? parseInt(lastEventId, 10) : 0
|
||||
return createSSEStream(c, sessionId, fromSeqNum)
|
||||
})
|
||||
|
||||
export default app;
|
||||
export default app
|
||||
|
||||
Reference in New Issue
Block a user