Some checks failed
CI / build-check-test (push) Has been cancelled
Restructure local extensions into per-feature directories, split WebUI into backend modules with slash commands and systemd support, and track prompts/skills under .pi/agent for portable Gitea deployment. Co-authored-by: Cursor <cursoragent@cursor.com>
125 lines
4.3 KiB
TypeScript
125 lines
4.3 KiB
TypeScript
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
import {
|
|
appendSessionName,
|
|
buildSessionListResponse,
|
|
deleteSessionFile,
|
|
pinSession,
|
|
readSessionMessages,
|
|
readSessionSummary,
|
|
resolveSessionFile,
|
|
} from "../services/sessions.ts";
|
|
import type { WebUiContext } from "../types/context.ts";
|
|
import { json, readBody } from "../http/request.ts";
|
|
|
|
export function handleSessionsRoute(
|
|
req: IncomingMessage,
|
|
res: ServerResponse,
|
|
ctx: WebUiContext,
|
|
pathname: string,
|
|
): boolean {
|
|
const { paths } = ctx.config;
|
|
const { sendCmd } = ctx.rpc;
|
|
|
|
if (req.method === "POST" && pathname === "/api/new-session") {
|
|
void sendCmd({ type: "new_session" })
|
|
.then(async (r: any) => {
|
|
if (!r.success) return json(res, { error: r.error }, 500);
|
|
if (r.data?.cancelled) return json(res, r.data);
|
|
const state = await sendCmd({ type: "get_state" });
|
|
const sessionFile = state.success ? state.data?.sessionFile : undefined;
|
|
return json(res, { ...r.data, sessionFile });
|
|
})
|
|
.catch((err) => json(res, { error: err.message }, 500));
|
|
return true;
|
|
}
|
|
|
|
if (req.method === "GET" && pathname === "/api/sessions") {
|
|
json(res, buildSessionListResponse(paths));
|
|
return true;
|
|
}
|
|
|
|
if (req.method === "POST" && pathname === "/api/sessions/history") {
|
|
void readBody(req)
|
|
.then(({ path: sp }) => {
|
|
const messages = readSessionMessages(sp as string);
|
|
const summary = readSessionSummary(paths, sp as string);
|
|
json(res, { messages, session: summary });
|
|
})
|
|
.catch((err) => json(res, { error: err.message }, 500));
|
|
return true;
|
|
}
|
|
|
|
if (req.method === "POST" && pathname === "/api/sessions/delete") {
|
|
void readBody(req)
|
|
.then(({ path: sp }) => {
|
|
deleteSessionFile(paths, sp as string);
|
|
json(res, { ok: true });
|
|
})
|
|
.catch((err) => json(res, { error: err.message }, 500));
|
|
return true;
|
|
}
|
|
|
|
if (req.method === "POST" && pathname === "/api/sessions/pin") {
|
|
void readBody(req)
|
|
.then(({ path: sp, pinned }) => {
|
|
const result = pinSession(paths, sp as string, Boolean(pinned));
|
|
json(res, { ok: true, ...result });
|
|
})
|
|
.catch((err) => json(res, { error: err.message }, 500));
|
|
return true;
|
|
}
|
|
|
|
if (req.method === "POST" && pathname === "/api/sessions/load") {
|
|
void readBody(req)
|
|
.then(async ({ path: sp }) => {
|
|
const sw = await sendCmd({ type: "switch_session", sessionPath: sp, cwdOverride: paths.repoRoot });
|
|
if (!sw.success) throw new Error(sw.error);
|
|
const mr = await sendCmd({ type: "get_messages" });
|
|
if (!mr.success) throw new Error(mr.error);
|
|
const summary = readSessionSummary(paths, sp as string);
|
|
json(res, { messages: mr.data.messages, session: summary });
|
|
})
|
|
.catch((err) => json(res, { error: err.message }, 500));
|
|
return true;
|
|
}
|
|
|
|
if (req.method === "POST" && pathname === "/api/sessions/activate") {
|
|
void readBody(req)
|
|
.then(async ({ path: sp }) => {
|
|
const sw = await sendCmd({ type: "switch_session", sessionPath: sp, cwdOverride: paths.repoRoot });
|
|
if (!sw.success) throw new Error(sw.error);
|
|
const state = await sendCmd({ type: "get_state" });
|
|
if (!state.success) throw new Error(state.error);
|
|
json(res, { ok: true, state: state.data });
|
|
})
|
|
.catch((err) => json(res, { error: err.message }, 500));
|
|
return true;
|
|
}
|
|
|
|
if (req.method === "POST" && pathname === "/api/sessions/name") {
|
|
void readBody(req)
|
|
.then(async ({ path: sp, name }) => {
|
|
if (typeof sp !== "string" || !sp.trim()) throw new Error("无效会话路径");
|
|
const savedName = appendSessionName(paths, sp, typeof name === "string" ? name : "");
|
|
const sessionPath = resolveSessionFile(paths, sp);
|
|
const state = await sendCmd({ type: "get_state" });
|
|
if (state.success && state.data?.sessionFile === sessionPath) {
|
|
const rename = await sendCmd({ type: "set_session_name", name: savedName });
|
|
if (!rename.success) throw new Error(rename.error || "同步当前会话名称失败");
|
|
}
|
|
json(res, { ok: true, name: savedName });
|
|
})
|
|
.catch((err) => json(res, { error: err.message }, 500));
|
|
return true;
|
|
}
|
|
|
|
if (req.method === "GET" && pathname === "/api/session-state") {
|
|
void sendCmd({ type: "get_state" })
|
|
.then((r: any) => (r.success ? json(res, r.data) : json(res, { error: r.error }, 500)))
|
|
.catch((err) => json(res, { error: err.message }, 500));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|