|
|
@@ -6,6 +6,7 @@ import { Worktree } from "../../worktree"
|
|
|
import { Instance } from "../../project/instance"
|
|
|
import { Project } from "../../project/project"
|
|
|
import { MCP } from "../../mcp"
|
|
|
+import { Session } from "../../session"
|
|
|
import { zodToJsonSchema } from "zod-to-json-schema"
|
|
|
import { errors } from "../error"
|
|
|
import { lazy } from "../../util/lazy"
|
|
|
@@ -184,6 +185,65 @@ export const ExperimentalRoutes = lazy(() =>
|
|
|
return c.json(true)
|
|
|
},
|
|
|
)
|
|
|
+ .get(
|
|
|
+ "/session",
|
|
|
+ describeRoute({
|
|
|
+ summary: "List sessions",
|
|
|
+ description:
|
|
|
+ "Get a list of all OpenCode sessions across projects, sorted by most recently updated. Archived sessions are excluded by default.",
|
|
|
+ operationId: "experimental.session.list",
|
|
|
+ responses: {
|
|
|
+ 200: {
|
|
|
+ description: "List of sessions",
|
|
|
+ content: {
|
|
|
+ "application/json": {
|
|
|
+ schema: resolver(Session.GlobalInfo.array()),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }),
|
|
|
+ validator(
|
|
|
+ "query",
|
|
|
+ z.object({
|
|
|
+ directory: z.string().optional().meta({ description: "Filter sessions by project directory" }),
|
|
|
+ roots: z.coerce.boolean().optional().meta({ description: "Only return root sessions (no parentID)" }),
|
|
|
+ start: z.coerce
|
|
|
+ .number()
|
|
|
+ .optional()
|
|
|
+ .meta({ description: "Filter sessions updated on or after this timestamp (milliseconds since epoch)" }),
|
|
|
+ cursor: z.coerce
|
|
|
+ .number()
|
|
|
+ .optional()
|
|
|
+ .meta({ description: "Return sessions updated before this timestamp (milliseconds since epoch)" }),
|
|
|
+ search: z.string().optional().meta({ description: "Filter sessions by title (case-insensitive)" }),
|
|
|
+ limit: z.coerce.number().optional().meta({ description: "Maximum number of sessions to return" }),
|
|
|
+ archived: z.coerce.boolean().optional().meta({ description: "Include archived sessions (default false)" }),
|
|
|
+ }),
|
|
|
+ ),
|
|
|
+ async (c) => {
|
|
|
+ const query = c.req.valid("query")
|
|
|
+ const limit = query.limit ?? 100
|
|
|
+ const sessions: Session.GlobalInfo[] = []
|
|
|
+ for await (const session of Session.listGlobal({
|
|
|
+ directory: query.directory,
|
|
|
+ roots: query.roots,
|
|
|
+ start: query.start,
|
|
|
+ cursor: query.cursor,
|
|
|
+ search: query.search,
|
|
|
+ limit: limit + 1,
|
|
|
+ archived: query.archived,
|
|
|
+ })) {
|
|
|
+ sessions.push(session)
|
|
|
+ }
|
|
|
+ const hasMore = sessions.length > limit
|
|
|
+ const list = hasMore ? sessions.slice(0, limit) : sessions
|
|
|
+ if (hasMore && list.length > 0) {
|
|
|
+ c.header("x-next-cursor", String(list[list.length - 1].time.updated))
|
|
|
+ }
|
|
|
+ return c.json(list)
|
|
|
+ },
|
|
|
+ )
|
|
|
.get(
|
|
|
"/resource",
|
|
|
describeRoute({
|