Skip to content

Commit 8b56d17

Browse files
authored
refactor(session): pass project to list (#25215)
1 parent 3c24d22 commit 8b56d17

6 files changed

Lines changed: 79 additions & 64 deletions

File tree

packages/opencode/src/cli/cmd/export.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,7 @@ export const ExportCommand = cmd({
245245
output: process.stderr,
246246
})
247247

248-
const sessions = []
249-
for await (const session of Session.list()) {
250-
sessions.push(session)
251-
}
248+
const sessions = await AppRuntime.runPromise(Session.Service.use((svc) => svc.list()))
252249

253250
if (sessions.length === 0) {
254251
prompts.log.error("No sessions found", {

packages/opencode/src/cli/cmd/session.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ export const SessionListCommand = cmd({
9191
},
9292
handler: async (args) => {
9393
await bootstrap(process.cwd(), async () => {
94-
const sessions = [...Session.list({ roots: true, limit: args.maxCount })]
94+
const sessions = await AppRuntime.runPromise(
95+
Session.Service.use((svc) => svc.list({ roots: true, limit: args.maxCount })),
96+
)
9597

9698
if (sessions.length === 0) {
9799
return

packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { Bus } from "@/bus"
55
import { Command } from "@/command"
66
import { Permission } from "@/permission"
77
import { PermissionID } from "@/permission/schema"
8-
import { Instance } from "@/project/instance"
98
import { SessionShare } from "@/share/session"
109
import { Session } from "@/session/session"
1110
import { SessionCompaction } from "@/session/compaction"
@@ -64,20 +63,15 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session",
6463
const scope = yield* Scope.Scope
6564

6665
const list = Effect.fn("SessionHttpApi.list")(function* (ctx: { query: typeof ListQuery.Type }) {
67-
const instance = yield* InstanceState.context
68-
return Instance.restore(instance, () =>
69-
Array.from(
70-
Session.list({
71-
directory: ctx.query.scope === "project" ? undefined : ctx.query.directory,
72-
scope: ctx.query.scope,
73-
path: ctx.query.path,
74-
roots: ctx.query.roots,
75-
start: ctx.query.start,
76-
search: ctx.query.search,
77-
limit: ctx.query.limit,
78-
}),
79-
),
80-
)
66+
return yield* session.list({
67+
directory: ctx.query.scope === "project" ? undefined : ctx.query.directory,
68+
scope: ctx.query.scope,
69+
path: ctx.query.path,
70+
roots: ctx.query.roots,
71+
start: ctx.query.start,
72+
search: ctx.query.search,
73+
limit: ctx.query.limit,
74+
})
8175
})
8276

8377
const status = Effect.fn("SessionHttpApi.status")(function* () {

packages/opencode/src/server/routes/instance/session.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,22 @@ export const SessionRoutes = lazy(() =>
7878
),
7979
async (c) => {
8080
const query = c.req.valid("query")
81-
const sessions: Session.Info[] = []
82-
for await (const session of Session.list({
83-
directory: query.scope === "project" ? undefined : query.directory,
84-
path: query.path,
85-
roots: queryBoolean(query.roots),
86-
start: query.start,
87-
search: query.search,
88-
limit: query.limit,
89-
})) {
90-
sessions.push(session)
91-
}
92-
return c.json(sessions)
81+
return c.json(
82+
await runRequest(
83+
"SessionRoutes.list",
84+
c,
85+
Session.Service.use((svc) =>
86+
svc.list({
87+
directory: query.scope === "project" ? undefined : query.directory,
88+
path: query.path,
89+
roots: queryBoolean(query.roots),
90+
start: query.start,
91+
search: query.search,
92+
limit: query.limit,
93+
}),
94+
),
95+
),
96+
)
9397
},
9498
)
9599
.get(

packages/opencode/src/session/session.ts

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { ProjectTable } from "../project/project.sql"
2626
import { Storage } from "@/storage/storage"
2727
import * as Log from "@opencode-ai/core/util/log"
2828
import { MessageV2 } from "./message-v2"
29-
import { Instance, type InstanceContext } from "../project/instance"
29+
import type { InstanceContext } from "../project/instance"
3030
import { InstanceState } from "@/effect/instance-state"
3131
import { Snapshot } from "@/snapshot"
3232
import { ProjectID } from "../project/schema"
@@ -234,6 +234,16 @@ export const MessagesInput = Schema.Struct({
234234
sessionID: SessionID,
235235
limit: Schema.optional(NonNegativeInt),
236236
}).pipe(withStatics((s) => ({ zod: zod(s) })))
237+
export type ListInput = {
238+
directory?: string
239+
scope?: "project"
240+
path?: string
241+
workspaceID?: WorkspaceID
242+
roots?: boolean
243+
start?: number
244+
search?: string
245+
limit?: number
246+
}
237247

238248
const CreatedEventSchema = Schema.Struct({
239249
sessionID: SessionID,
@@ -390,6 +400,7 @@ export class BusyError extends Error {
390400
}
391401

392402
export interface Interface {
403+
readonly list: (input?: ListInput) => Effect.Effect<Info[]>
393404
readonly create: (input?: {
394405
parentID?: SessionID
395406
title?: string
@@ -498,6 +509,11 @@ export const layer: Layer.Layer<Service, never, Bus.Service | Storage.Service |
498509
return fromRow(row)
499510
})
500511

512+
const list = Effect.fn("Session.list")(function* (input?: ListInput) {
513+
const ctx = yield* InstanceState.context
514+
return Array.from(listByProject({ projectID: ctx.project.id, ...(input ?? {}) }))
515+
})
516+
501517
const children = Effect.fn("Session.children")(function* (parentID: SessionID) {
502518
const rows = yield* db((d) =>
503519
d
@@ -731,6 +747,7 @@ export const layer: Layer.Layer<Service, never, Bus.Service | Storage.Service |
731747
})
732748

733749
return Service.of({
750+
list,
734751
create,
735752
fork,
736753
touch,
@@ -762,23 +779,15 @@ export const defaultLayer = layer.pipe(
762779
Layer.provide(SyncEvent.defaultLayer),
763780
)
764781

765-
export function* list(input?: {
766-
directory?: string
767-
scope?: "project"
768-
path?: string
769-
workspaceID?: WorkspaceID
770-
roots?: boolean
771-
start?: number
772-
search?: string
773-
limit?: number
782+
function* listByProject(input: ListInput & {
783+
projectID: ProjectID
774784
}) {
775-
const project = Instance.project
776-
const conditions = [eq(SessionTable.project_id, project.id)]
785+
const conditions = [eq(SessionTable.project_id, input.projectID)]
777786

778-
if (input?.workspaceID) {
787+
if (input.workspaceID) {
779788
conditions.push(eq(SessionTable.workspace_id, input.workspaceID))
780789
}
781-
if (input?.path !== undefined) {
790+
if (input.path !== undefined) {
782791
if (input.path) {
783792
const conds = [eq(SessionTable.path, input.path), like(SessionTable.path, `${input.path}/%`)]
784793

@@ -788,22 +797,22 @@ export function* list(input?: {
788797
: or(...conds)!,
789798
)
790799
}
791-
} else if (input?.scope !== "project" && !Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) {
792-
if (input?.directory) {
800+
} else if (input.scope !== "project" && !Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) {
801+
if (input.directory) {
793802
conditions.push(eq(SessionTable.directory, input.directory))
794803
}
795804
}
796-
if (input?.roots) {
805+
if (input.roots) {
797806
conditions.push(isNull(SessionTable.parent_id))
798807
}
799-
if (input?.start) {
808+
if (input.start) {
800809
conditions.push(gte(SessionTable.time_updated, input.start))
801810
}
802-
if (input?.search) {
811+
if (input.search) {
803812
conditions.push(like(SessionTable.title, `%${input.search}%`))
804813
}
805814

806-
const limit = input?.limit ?? 100
815+
const limit = input.limit ?? 100
807816

808817
const rows = Database.use((db) =>
809818
db

packages/opencode/test/server/session-list.test.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ const svc = {
2323
create(input?: SessionNs.CreateInput) {
2424
return run(SessionNs.Service.use((svc) => svc.create(input)))
2525
},
26+
list(input?: SessionNs.ListInput) {
27+
return run(SessionNs.Service.use((svc) => svc.list(input)))
28+
},
2629
}
2730

2831
afterEach(async () => {
@@ -55,7 +58,7 @@ describe("session.list", () => {
5558
fn: async () => svc.create({ title: "sibling" }),
5659
})
5760

58-
const ids = [...svc.list()].map((s) => s.id)
61+
const ids = (await svc.list()).map((s) => s.id)
5962
expect(ids).toContain(root.id)
6063
expect(ids).toContain(parent.id)
6164
expect(ids).toContain(current.id)
@@ -88,7 +91,7 @@ describe("session.list", () => {
8891
fn: async () => svc.create({ title: "sibling" }),
8992
})
9093

91-
const ids = [...svc.list({ directory: path.join(tmp.path, "packages", "opencode") })].map((s) => s.id)
94+
const ids = (await svc.list({ directory: path.join(tmp.path, "packages", "opencode") })).map((s) => s.id)
9295
expect(ids).not.toContain(root.id)
9396
expect(ids).not.toContain(parent.id)
9497
expect(ids).toContain(current.id)
@@ -123,9 +126,12 @@ describe("session.list", () => {
123126
fn: async () => svc.create({ title: "sibling" }),
124127
})
125128

126-
const pathIDs = [
127-
...svc.list({ directory: path.join(tmp.path, "packages", "app"), path: "packages/opencode/src" }),
128-
].map((s) => s.id)
129+
const pathIDs = (
130+
await svc.list({
131+
directory: path.join(tmp.path, "packages", "app"),
132+
path: "packages/opencode/src",
133+
})
134+
).map((s) => s.id)
129135
expect(pathIDs).not.toContain(parent.id)
130136
expect(pathIDs).toContain(current.id)
131137
expect(pathIDs).toContain(deeper.id)
@@ -155,9 +161,12 @@ describe("session.list", () => {
155161
Database.use((db) => db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, current.id)).run())
156162
Database.use((db) => db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, sibling.id)).run())
157163

158-
const pathIDs = [
159-
...svc.list({ directory: path.join(tmp.path, "packages", "opencode", "src"), path: "packages/opencode/src" }),
160-
].map((s) => s.id)
164+
const pathIDs = (
165+
await svc.list({
166+
directory: path.join(tmp.path, "packages", "opencode", "src"),
167+
path: "packages/opencode/src",
168+
})
169+
).map((s) => s.id)
161170
expect(pathIDs).toContain(current.id)
162171
expect(pathIDs).not.toContain(sibling.id)
163172
},
@@ -172,7 +181,7 @@ describe("session.list", () => {
172181
const root = await svc.create({ title: "root-session" })
173182
const child = await svc.create({ title: "child-session", parentID: root.id })
174183

175-
const sessions = [...svc.list({ roots: true })]
184+
const sessions = await svc.list({ roots: true })
176185
const ids = sessions.map((s) => s.id)
177186

178187
expect(ids).toContain(root.id)
@@ -189,7 +198,7 @@ describe("session.list", () => {
189198
await svc.create({ title: "new-session" })
190199
const futureStart = Date.now() + 86400000
191200

192-
const sessions = [...svc.list({ start: futureStart })]
201+
const sessions = await svc.list({ start: futureStart })
193202
expect(sessions.length).toBe(0)
194203
},
195204
})
@@ -203,7 +212,7 @@ describe("session.list", () => {
203212
await svc.create({ title: "unique-search-term-abc" })
204213
await svc.create({ title: "other-session-xyz" })
205214

206-
const sessions = [...svc.list({ search: "unique-search" })]
215+
const sessions = await svc.list({ search: "unique-search" })
207216
const titles = sessions.map((s) => s.title)
208217

209218
expect(titles).toContain("unique-search-term-abc")
@@ -221,7 +230,7 @@ describe("session.list", () => {
221230
await svc.create({ title: "session-2" })
222231
await svc.create({ title: "session-3" })
223232

224-
const sessions = [...svc.list({ limit: 2 })]
233+
const sessions = await svc.list({ limit: 2 })
225234
expect(sessions.length).toBe(2)
226235
},
227236
})

0 commit comments

Comments
 (0)