feat: 支持自托管的 remote-control-server (#214)

* feat: 支持自托管的 remote-control-server (#214)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This commit is contained in:
claude-code-best
2026-04-09 17:40:50 +08:00
committed by GitHub
parent f17b7c7163
commit 2da6514095
81 changed files with 9875 additions and 40 deletions

View File

@@ -0,0 +1,85 @@
import { Hono } from "hono";
import {
createSession,
getSession,
updateSessionTitle,
archiveSession,
} from "../../services/session";
import { createWorkItem } from "../../services/work-dispatch";
import { apiKeyAuth, acceptCliHeaders } from "../../auth/middleware";
import { publishSessionEvent } from "../../services/transport";
const app = new Hono();
/** POST /v1/sessions — Create session */
app.post("/", acceptCliHeaders, apiKeyAuth, async (c) => {
const body = await c.req.json();
const username = c.get("username");
const session = createSession({ ...body, username });
// Create work item if environment is specified
if (body.environment_id) {
try {
await createWorkItem(body.environment_id, session.id);
} catch (err) {
console.error(`[RCS] Failed to create work item: ${(err as Error).message}`);
}
}
// Publish initial events if provided
if (body.events && Array.isArray(body.events)) {
for (const evt of body.events) {
publishSessionEvent(session.id, evt.type || "init", evt, "outbound");
}
}
return c.json(session, 200);
});
/** GET /v1/sessions/:id — Get session */
app.get("/:id", acceptCliHeaders, apiKeyAuth, async (c) => {
const session = getSession(c.req.param("id"));
if (!session) {
return c.json({ error: { type: "not_found", message: "Session not found" } }, 404);
}
return c.json(session, 200);
});
/** PATCH /v1/sessions/:id — Update session title */
app.patch("/:id", acceptCliHeaders, apiKeyAuth, async (c) => {
const body = await c.req.json();
if (body.title) {
updateSessionTitle(c.req.param("id"), body.title);
}
const session = getSession(c.req.param("id"));
return c.json(session, 200);
});
/** POST /v1/sessions/:id/archive — Archive session */
app.post("/:id/archive", acceptCliHeaders, apiKeyAuth, async (c) => {
try {
archiveSession(c.req.param("id"));
} catch {
return c.json({ status: "ok" }, 409);
}
return c.json({ status: "ok" }, 200);
});
/** POST /v1/sessions/:id/events — Send event to session */
app.post("/:id/events", acceptCliHeaders, apiKeyAuth, async (c) => {
const sessionId = c.req.param("id");
const body = await c.req.json();
const events = body.events
? Array.isArray(body.events) ? body.events : [body.events]
: Array.isArray(body) ? body : [body];
const published = [];
for (const evt of events) {
const result = publishSessionEvent(sessionId, evt.type || "message", evt, "inbound");
published.push(result);
}
return c.json({ status: "ok", events: published.length }, 200);
});
export default app;