refactor(session): pass project to list (#25215)
This commit is contained in:
@@ -245,10 +245,7 @@ export const ExportCommand = cmd({
|
|||||||
output: process.stderr,
|
output: process.stderr,
|
||||||
})
|
})
|
||||||
|
|
||||||
const sessions = []
|
const sessions = await AppRuntime.runPromise(Session.Service.use((svc) => svc.list()))
|
||||||
for await (const session of Session.list()) {
|
|
||||||
sessions.push(session)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessions.length === 0) {
|
if (sessions.length === 0) {
|
||||||
prompts.log.error("No sessions found", {
|
prompts.log.error("No sessions found", {
|
||||||
|
|||||||
@@ -91,7 +91,9 @@ export const SessionListCommand = cmd({
|
|||||||
},
|
},
|
||||||
handler: async (args) => {
|
handler: async (args) => {
|
||||||
await bootstrap(process.cwd(), async () => {
|
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) {
|
if (sessions.length === 0) {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { Bus } from "@/bus"
|
|||||||
import { Command } from "@/command"
|
import { Command } from "@/command"
|
||||||
import { Permission } from "@/permission"
|
import { Permission } from "@/permission"
|
||||||
import { PermissionID } from "@/permission/schema"
|
import { PermissionID } from "@/permission/schema"
|
||||||
import { Instance } from "@/project/instance"
|
|
||||||
import { SessionShare } from "@/share/session"
|
import { SessionShare } from "@/share/session"
|
||||||
import { Session } from "@/session/session"
|
import { Session } from "@/session/session"
|
||||||
import { SessionCompaction } from "@/session/compaction"
|
import { SessionCompaction } from "@/session/compaction"
|
||||||
@@ -64,20 +63,15 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session",
|
|||||||
const scope = yield* Scope.Scope
|
const scope = yield* Scope.Scope
|
||||||
|
|
||||||
const list = Effect.fn("SessionHttpApi.list")(function* (ctx: { query: typeof ListQuery.Type }) {
|
const list = Effect.fn("SessionHttpApi.list")(function* (ctx: { query: typeof ListQuery.Type }) {
|
||||||
const instance = yield* InstanceState.context
|
return yield* session.list({
|
||||||
return Instance.restore(instance, () =>
|
directory: ctx.query.scope === "project" ? undefined : ctx.query.directory,
|
||||||
Array.from(
|
scope: ctx.query.scope,
|
||||||
Session.list({
|
path: ctx.query.path,
|
||||||
directory: ctx.query.scope === "project" ? undefined : ctx.query.directory,
|
roots: ctx.query.roots,
|
||||||
scope: ctx.query.scope,
|
start: ctx.query.start,
|
||||||
path: ctx.query.path,
|
search: ctx.query.search,
|
||||||
roots: ctx.query.roots,
|
limit: ctx.query.limit,
|
||||||
start: ctx.query.start,
|
})
|
||||||
search: ctx.query.search,
|
|
||||||
limit: ctx.query.limit,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const status = Effect.fn("SessionHttpApi.status")(function* () {
|
const status = Effect.fn("SessionHttpApi.status")(function* () {
|
||||||
|
|||||||
@@ -78,18 +78,22 @@ export const SessionRoutes = lazy(() =>
|
|||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const query = c.req.valid("query")
|
const query = c.req.valid("query")
|
||||||
const sessions: Session.Info[] = []
|
return c.json(
|
||||||
for await (const session of Session.list({
|
await runRequest(
|
||||||
directory: query.scope === "project" ? undefined : query.directory,
|
"SessionRoutes.list",
|
||||||
path: query.path,
|
c,
|
||||||
roots: queryBoolean(query.roots),
|
Session.Service.use((svc) =>
|
||||||
start: query.start,
|
svc.list({
|
||||||
search: query.search,
|
directory: query.scope === "project" ? undefined : query.directory,
|
||||||
limit: query.limit,
|
path: query.path,
|
||||||
})) {
|
roots: queryBoolean(query.roots),
|
||||||
sessions.push(session)
|
start: query.start,
|
||||||
}
|
search: query.search,
|
||||||
return c.json(sessions)
|
limit: query.limit,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.get(
|
.get(
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import { ProjectTable } from "../project/project.sql"
|
|||||||
import { Storage } from "@/storage/storage"
|
import { Storage } from "@/storage/storage"
|
||||||
import * as Log from "@opencode-ai/core/util/log"
|
import * as Log from "@opencode-ai/core/util/log"
|
||||||
import { MessageV2 } from "./message-v2"
|
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 { InstanceState } from "@/effect/instance-state"
|
||||||
import { Snapshot } from "@/snapshot"
|
import { Snapshot } from "@/snapshot"
|
||||||
import { ProjectID } from "../project/schema"
|
import { ProjectID } from "../project/schema"
|
||||||
@@ -234,6 +234,16 @@ export const MessagesInput = Schema.Struct({
|
|||||||
sessionID: SessionID,
|
sessionID: SessionID,
|
||||||
limit: Schema.optional(NonNegativeInt),
|
limit: Schema.optional(NonNegativeInt),
|
||||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
}).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({
|
const CreatedEventSchema = Schema.Struct({
|
||||||
sessionID: SessionID,
|
sessionID: SessionID,
|
||||||
@@ -390,6 +400,7 @@ export class BusyError extends Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Interface {
|
export interface Interface {
|
||||||
|
readonly list: (input?: ListInput) => Effect.Effect<Info[]>
|
||||||
readonly create: (input?: {
|
readonly create: (input?: {
|
||||||
parentID?: SessionID
|
parentID?: SessionID
|
||||||
title?: string
|
title?: string
|
||||||
@@ -498,6 +509,11 @@ export const layer: Layer.Layer<Service, never, Bus.Service | Storage.Service |
|
|||||||
return fromRow(row)
|
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 children = Effect.fn("Session.children")(function* (parentID: SessionID) {
|
||||||
const rows = yield* db((d) =>
|
const rows = yield* db((d) =>
|
||||||
d
|
d
|
||||||
@@ -731,6 +747,7 @@ export const layer: Layer.Layer<Service, never, Bus.Service | Storage.Service |
|
|||||||
})
|
})
|
||||||
|
|
||||||
return Service.of({
|
return Service.of({
|
||||||
|
list,
|
||||||
create,
|
create,
|
||||||
fork,
|
fork,
|
||||||
touch,
|
touch,
|
||||||
@@ -762,23 +779,15 @@ export const defaultLayer = layer.pipe(
|
|||||||
Layer.provide(SyncEvent.defaultLayer),
|
Layer.provide(SyncEvent.defaultLayer),
|
||||||
)
|
)
|
||||||
|
|
||||||
export function* list(input?: {
|
function* listByProject(input: ListInput & {
|
||||||
directory?: string
|
projectID: ProjectID
|
||||||
scope?: "project"
|
|
||||||
path?: string
|
|
||||||
workspaceID?: WorkspaceID
|
|
||||||
roots?: boolean
|
|
||||||
start?: number
|
|
||||||
search?: string
|
|
||||||
limit?: number
|
|
||||||
}) {
|
}) {
|
||||||
const project = Instance.project
|
const conditions = [eq(SessionTable.project_id, input.projectID)]
|
||||||
const conditions = [eq(SessionTable.project_id, project.id)]
|
|
||||||
|
|
||||||
if (input?.workspaceID) {
|
if (input.workspaceID) {
|
||||||
conditions.push(eq(SessionTable.workspace_id, input.workspaceID))
|
conditions.push(eq(SessionTable.workspace_id, input.workspaceID))
|
||||||
}
|
}
|
||||||
if (input?.path !== undefined) {
|
if (input.path !== undefined) {
|
||||||
if (input.path) {
|
if (input.path) {
|
||||||
const conds = [eq(SessionTable.path, input.path), like(SessionTable.path, `${input.path}/%`)]
|
const conds = [eq(SessionTable.path, input.path), like(SessionTable.path, `${input.path}/%`)]
|
||||||
|
|
||||||
@@ -788,22 +797,22 @@ export function* list(input?: {
|
|||||||
: or(...conds)!,
|
: or(...conds)!,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (input?.scope !== "project" && !Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) {
|
} else if (input.scope !== "project" && !Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) {
|
||||||
if (input?.directory) {
|
if (input.directory) {
|
||||||
conditions.push(eq(SessionTable.directory, input.directory))
|
conditions.push(eq(SessionTable.directory, input.directory))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (input?.roots) {
|
if (input.roots) {
|
||||||
conditions.push(isNull(SessionTable.parent_id))
|
conditions.push(isNull(SessionTable.parent_id))
|
||||||
}
|
}
|
||||||
if (input?.start) {
|
if (input.start) {
|
||||||
conditions.push(gte(SessionTable.time_updated, input.start))
|
conditions.push(gte(SessionTable.time_updated, input.start))
|
||||||
}
|
}
|
||||||
if (input?.search) {
|
if (input.search) {
|
||||||
conditions.push(like(SessionTable.title, `%${input.search}%`))
|
conditions.push(like(SessionTable.title, `%${input.search}%`))
|
||||||
}
|
}
|
||||||
|
|
||||||
const limit = input?.limit ?? 100
|
const limit = input.limit ?? 100
|
||||||
|
|
||||||
const rows = Database.use((db) =>
|
const rows = Database.use((db) =>
|
||||||
db
|
db
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ const svc = {
|
|||||||
create(input?: SessionNs.CreateInput) {
|
create(input?: SessionNs.CreateInput) {
|
||||||
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
||||||
},
|
},
|
||||||
|
list(input?: SessionNs.ListInput) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.list(input)))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@@ -55,7 +58,7 @@ describe("session.list", () => {
|
|||||||
fn: async () => svc.create({ title: "sibling" }),
|
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(root.id)
|
||||||
expect(ids).toContain(parent.id)
|
expect(ids).toContain(parent.id)
|
||||||
expect(ids).toContain(current.id)
|
expect(ids).toContain(current.id)
|
||||||
@@ -88,7 +91,7 @@ describe("session.list", () => {
|
|||||||
fn: async () => svc.create({ title: "sibling" }),
|
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(root.id)
|
||||||
expect(ids).not.toContain(parent.id)
|
expect(ids).not.toContain(parent.id)
|
||||||
expect(ids).toContain(current.id)
|
expect(ids).toContain(current.id)
|
||||||
@@ -123,9 +126,12 @@ describe("session.list", () => {
|
|||||||
fn: async () => svc.create({ title: "sibling" }),
|
fn: async () => svc.create({ title: "sibling" }),
|
||||||
})
|
})
|
||||||
|
|
||||||
const pathIDs = [
|
const pathIDs = (
|
||||||
...svc.list({ directory: path.join(tmp.path, "packages", "app"), path: "packages/opencode/src" }),
|
await svc.list({
|
||||||
].map((s) => s.id)
|
directory: path.join(tmp.path, "packages", "app"),
|
||||||
|
path: "packages/opencode/src",
|
||||||
|
})
|
||||||
|
).map((s) => s.id)
|
||||||
expect(pathIDs).not.toContain(parent.id)
|
expect(pathIDs).not.toContain(parent.id)
|
||||||
expect(pathIDs).toContain(current.id)
|
expect(pathIDs).toContain(current.id)
|
||||||
expect(pathIDs).toContain(deeper.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, current.id)).run())
|
||||||
Database.use((db) => db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, sibling.id)).run())
|
Database.use((db) => db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, sibling.id)).run())
|
||||||
|
|
||||||
const pathIDs = [
|
const pathIDs = (
|
||||||
...svc.list({ directory: path.join(tmp.path, "packages", "opencode", "src"), path: "packages/opencode/src" }),
|
await svc.list({
|
||||||
].map((s) => s.id)
|
directory: path.join(tmp.path, "packages", "opencode", "src"),
|
||||||
|
path: "packages/opencode/src",
|
||||||
|
})
|
||||||
|
).map((s) => s.id)
|
||||||
expect(pathIDs).toContain(current.id)
|
expect(pathIDs).toContain(current.id)
|
||||||
expect(pathIDs).not.toContain(sibling.id)
|
expect(pathIDs).not.toContain(sibling.id)
|
||||||
},
|
},
|
||||||
@@ -172,7 +181,7 @@ describe("session.list", () => {
|
|||||||
const root = await svc.create({ title: "root-session" })
|
const root = await svc.create({ title: "root-session" })
|
||||||
const child = await svc.create({ title: "child-session", parentID: root.id })
|
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)
|
const ids = sessions.map((s) => s.id)
|
||||||
|
|
||||||
expect(ids).toContain(root.id)
|
expect(ids).toContain(root.id)
|
||||||
@@ -189,7 +198,7 @@ describe("session.list", () => {
|
|||||||
await svc.create({ title: "new-session" })
|
await svc.create({ title: "new-session" })
|
||||||
const futureStart = Date.now() + 86400000
|
const futureStart = Date.now() + 86400000
|
||||||
|
|
||||||
const sessions = [...svc.list({ start: futureStart })]
|
const sessions = await svc.list({ start: futureStart })
|
||||||
expect(sessions.length).toBe(0)
|
expect(sessions.length).toBe(0)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -203,7 +212,7 @@ describe("session.list", () => {
|
|||||||
await svc.create({ title: "unique-search-term-abc" })
|
await svc.create({ title: "unique-search-term-abc" })
|
||||||
await svc.create({ title: "other-session-xyz" })
|
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)
|
const titles = sessions.map((s) => s.title)
|
||||||
|
|
||||||
expect(titles).toContain("unique-search-term-abc")
|
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-2" })
|
||||||
await svc.create({ title: "session-3" })
|
await svc.create({ title: "session-3" })
|
||||||
|
|
||||||
const sessions = [...svc.list({ limit: 2 })]
|
const sessions = await svc.list({ limit: 2 })
|
||||||
expect(sessions.length).toBe(2)
|
expect(sessions.length).toBe(2)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user