Pārlūkot izejas kodu

refactor(session): pass project to list (#25215)

Kit Langton 3 mēneši atpakaļ
vecāks
revīzija
8b56d1712f

+ 1 - 4
packages/opencode/src/cli/cmd/export.ts

@@ -245,10 +245,7 @@ export const ExportCommand = cmd({
           output: process.stderr,
         })
 
-        const sessions = []
-        for await (const session of Session.list()) {
-          sessions.push(session)
-        }
+        const sessions = await AppRuntime.runPromise(Session.Service.use((svc) => svc.list()))
 
         if (sessions.length === 0) {
           prompts.log.error("No sessions found", {

+ 3 - 1
packages/opencode/src/cli/cmd/session.ts

@@ -91,7 +91,9 @@ export const SessionListCommand = cmd({
   },
   handler: async (args) => {
     await bootstrap(process.cwd(), async () => {
-      const sessions = [...Session.list({ roots: true, limit: args.maxCount })]
+      const sessions = await AppRuntime.runPromise(
+        Session.Service.use((svc) => svc.list({ roots: true, limit: args.maxCount })),
+      )
 
       if (sessions.length === 0) {
         return

+ 9 - 15
packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts

@@ -5,7 +5,6 @@ import { Bus } from "@/bus"
 import { Command } from "@/command"
 import { Permission } from "@/permission"
 import { PermissionID } from "@/permission/schema"
-import { Instance } from "@/project/instance"
 import { SessionShare } from "@/share/session"
 import { Session } from "@/session/session"
 import { SessionCompaction } from "@/session/compaction"
@@ -64,20 +63,15 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session",
     const scope = yield* Scope.Scope
 
     const list = Effect.fn("SessionHttpApi.list")(function* (ctx: { query: typeof ListQuery.Type }) {
-      const instance = yield* InstanceState.context
-      return Instance.restore(instance, () =>
-        Array.from(
-          Session.list({
-            directory: ctx.query.scope === "project" ? undefined : ctx.query.directory,
-            scope: ctx.query.scope,
-            path: ctx.query.path,
-            roots: ctx.query.roots,
-            start: ctx.query.start,
-            search: ctx.query.search,
-            limit: ctx.query.limit,
-          }),
-        ),
-      )
+      return yield* session.list({
+        directory: ctx.query.scope === "project" ? undefined : ctx.query.directory,
+        scope: ctx.query.scope,
+        path: ctx.query.path,
+        roots: ctx.query.roots,
+        start: ctx.query.start,
+        search: ctx.query.search,
+        limit: ctx.query.limit,
+      })
     })
 
     const status = Effect.fn("SessionHttpApi.status")(function* () {

+ 16 - 12
packages/opencode/src/server/routes/instance/session.ts

@@ -78,18 +78,22 @@ export const SessionRoutes = lazy(() =>
       ),
       async (c) => {
         const query = c.req.valid("query")
-        const sessions: Session.Info[] = []
-        for await (const session of Session.list({
-          directory: query.scope === "project" ? undefined : query.directory,
-          path: query.path,
-          roots: queryBoolean(query.roots),
-          start: query.start,
-          search: query.search,
-          limit: query.limit,
-        })) {
-          sessions.push(session)
-        }
-        return c.json(sessions)
+        return c.json(
+          await runRequest(
+            "SessionRoutes.list",
+            c,
+            Session.Service.use((svc) =>
+              svc.list({
+                directory: query.scope === "project" ? undefined : query.directory,
+                path: query.path,
+                roots: queryBoolean(query.roots),
+                start: query.start,
+                search: query.search,
+                limit: query.limit,
+              }),
+            ),
+          ),
+        )
       },
     )
     .get(

+ 29 - 20
packages/opencode/src/session/session.ts

@@ -26,7 +26,7 @@ import { ProjectTable } from "../project/project.sql"
 import { Storage } from "@/storage/storage"
 import * as Log from "@opencode-ai/core/util/log"
 import { MessageV2 } from "./message-v2"
-import { Instance, type InstanceContext } from "../project/instance"
+import type { InstanceContext } from "../project/instance"
 import { InstanceState } from "@/effect/instance-state"
 import { Snapshot } from "@/snapshot"
 import { ProjectID } from "../project/schema"
@@ -234,6 +234,16 @@ export const MessagesInput = Schema.Struct({
   sessionID: SessionID,
   limit: Schema.optional(NonNegativeInt),
 }).pipe(withStatics((s) => ({ zod: zod(s) })))
+export type ListInput = {
+  directory?: string
+  scope?: "project"
+  path?: string
+  workspaceID?: WorkspaceID
+  roots?: boolean
+  start?: number
+  search?: string
+  limit?: number
+}
 
 const CreatedEventSchema = Schema.Struct({
   sessionID: SessionID,
@@ -390,6 +400,7 @@ export class BusyError extends Error {
 }
 
 export interface Interface {
+  readonly list: (input?: ListInput) => Effect.Effect<Info[]>
   readonly create: (input?: {
     parentID?: SessionID
     title?: string
@@ -498,6 +509,11 @@ export const layer: Layer.Layer<Service, never, Bus.Service | Storage.Service |
       return fromRow(row)
     })
 
+    const list = Effect.fn("Session.list")(function* (input?: ListInput) {
+      const ctx = yield* InstanceState.context
+      return Array.from(listByProject({ projectID: ctx.project.id, ...(input ?? {}) }))
+    })
+
     const children = Effect.fn("Session.children")(function* (parentID: SessionID) {
       const rows = yield* db((d) =>
         d
@@ -731,6 +747,7 @@ export const layer: Layer.Layer<Service, never, Bus.Service | Storage.Service |
     })
 
     return Service.of({
+      list,
       create,
       fork,
       touch,
@@ -762,23 +779,15 @@ export const defaultLayer = layer.pipe(
   Layer.provide(SyncEvent.defaultLayer),
 )
 
-export function* list(input?: {
-  directory?: string
-  scope?: "project"
-  path?: string
-  workspaceID?: WorkspaceID
-  roots?: boolean
-  start?: number
-  search?: string
-  limit?: number
+function* listByProject(input: ListInput & {
+  projectID: ProjectID
 }) {
-  const project = Instance.project
-  const conditions = [eq(SessionTable.project_id, project.id)]
+  const conditions = [eq(SessionTable.project_id, input.projectID)]
 
-  if (input?.workspaceID) {
+  if (input.workspaceID) {
     conditions.push(eq(SessionTable.workspace_id, input.workspaceID))
   }
-  if (input?.path !== undefined) {
+  if (input.path !== undefined) {
     if (input.path) {
       const conds = [eq(SessionTable.path, input.path), like(SessionTable.path, `${input.path}/%`)]
 
@@ -788,22 +797,22 @@ export function* list(input?: {
           : or(...conds)!,
       )
     }
-  } else if (input?.scope !== "project" && !Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) {
-    if (input?.directory) {
+  } else if (input.scope !== "project" && !Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) {
+    if (input.directory) {
       conditions.push(eq(SessionTable.directory, input.directory))
     }
   }
-  if (input?.roots) {
+  if (input.roots) {
     conditions.push(isNull(SessionTable.parent_id))
   }
-  if (input?.start) {
+  if (input.start) {
     conditions.push(gte(SessionTable.time_updated, input.start))
   }
-  if (input?.search) {
+  if (input.search) {
     conditions.push(like(SessionTable.title, `%${input.search}%`))
   }
 
-  const limit = input?.limit ?? 100
+  const limit = input.limit ?? 100
 
   const rows = Database.use((db) =>
     db

+ 21 - 12
packages/opencode/test/server/session-list.test.ts

@@ -23,6 +23,9 @@ const svc = {
   create(input?: SessionNs.CreateInput) {
     return run(SessionNs.Service.use((svc) => svc.create(input)))
   },
+  list(input?: SessionNs.ListInput) {
+    return run(SessionNs.Service.use((svc) => svc.list(input)))
+  },
 }
 
 afterEach(async () => {
@@ -55,7 +58,7 @@ describe("session.list", () => {
           fn: async () => svc.create({ title: "sibling" }),
         })
 
-        const ids = [...svc.list()].map((s) => s.id)
+        const ids = (await svc.list()).map((s) => s.id)
         expect(ids).toContain(root.id)
         expect(ids).toContain(parent.id)
         expect(ids).toContain(current.id)
@@ -88,7 +91,7 @@ describe("session.list", () => {
           fn: async () => svc.create({ title: "sibling" }),
         })
 
-        const ids = [...svc.list({ directory: path.join(tmp.path, "packages", "opencode") })].map((s) => s.id)
+        const ids = (await svc.list({ directory: path.join(tmp.path, "packages", "opencode") })).map((s) => s.id)
         expect(ids).not.toContain(root.id)
         expect(ids).not.toContain(parent.id)
         expect(ids).toContain(current.id)
@@ -123,9 +126,12 @@ describe("session.list", () => {
           fn: async () => svc.create({ title: "sibling" }),
         })
 
-        const pathIDs = [
-          ...svc.list({ directory: path.join(tmp.path, "packages", "app"), path: "packages/opencode/src" }),
-        ].map((s) => s.id)
+        const pathIDs = (
+          await svc.list({
+            directory: path.join(tmp.path, "packages", "app"),
+            path: "packages/opencode/src",
+          })
+        ).map((s) => s.id)
         expect(pathIDs).not.toContain(parent.id)
         expect(pathIDs).toContain(current.id)
         expect(pathIDs).toContain(deeper.id)
@@ -155,9 +161,12 @@ describe("session.list", () => {
         Database.use((db) => db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, current.id)).run())
         Database.use((db) => db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, sibling.id)).run())
 
-        const pathIDs = [
-          ...svc.list({ directory: path.join(tmp.path, "packages", "opencode", "src"), path: "packages/opencode/src" }),
-        ].map((s) => s.id)
+        const pathIDs = (
+          await svc.list({
+            directory: path.join(tmp.path, "packages", "opencode", "src"),
+            path: "packages/opencode/src",
+          })
+        ).map((s) => s.id)
         expect(pathIDs).toContain(current.id)
         expect(pathIDs).not.toContain(sibling.id)
       },
@@ -172,7 +181,7 @@ describe("session.list", () => {
         const root = await svc.create({ title: "root-session" })
         const child = await svc.create({ title: "child-session", parentID: root.id })
 
-        const sessions = [...svc.list({ roots: true })]
+        const sessions = await svc.list({ roots: true })
         const ids = sessions.map((s) => s.id)
 
         expect(ids).toContain(root.id)
@@ -189,7 +198,7 @@ describe("session.list", () => {
         await svc.create({ title: "new-session" })
         const futureStart = Date.now() + 86400000
 
-        const sessions = [...svc.list({ start: futureStart })]
+        const sessions = await svc.list({ start: futureStart })
         expect(sessions.length).toBe(0)
       },
     })
@@ -203,7 +212,7 @@ describe("session.list", () => {
         await svc.create({ title: "unique-search-term-abc" })
         await svc.create({ title: "other-session-xyz" })
 
-        const sessions = [...svc.list({ search: "unique-search" })]
+        const sessions = await svc.list({ search: "unique-search" })
         const titles = sessions.map((s) => s.title)
 
         expect(titles).toContain("unique-search-term-abc")
@@ -221,7 +230,7 @@ describe("session.list", () => {
         await svc.create({ title: "session-2" })
         await svc.create({ title: "session-3" })
 
-        const sessions = [...svc.list({ limit: 2 })]
+        const sessions = await svc.list({ limit: 2 })
         expect(sessions.length).toBe(2)
       },
     })