feat(core): include resolved location info
This commit is contained in:
parent
332366a024
commit
0bd61d2826
20 changed files with 92 additions and 82 deletions
|
|
@ -30,7 +30,7 @@ export type Payload<D extends Definition = Definition> = {
|
|||
readonly type: D["type"]
|
||||
readonly data: Data<D>
|
||||
readonly version?: number
|
||||
readonly location?: Location.Ref
|
||||
readonly location?: Location.Info
|
||||
readonly metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ export function define<const Type extends string, Fields extends Schema.Struct.F
|
|||
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
|
||||
type: Schema.Literal(input.type),
|
||||
version: Schema.optional(Schema.Number),
|
||||
location: Schema.optional(Location.Ref),
|
||||
location: Schema.optional(Location.Info),
|
||||
data: Data,
|
||||
}).annotate({ identifier: input.type })
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ export function definitions() {
|
|||
export interface PublishOptions {
|
||||
readonly id?: ID
|
||||
readonly metadata?: Record<string, unknown>
|
||||
readonly location?: Location.Ref
|
||||
readonly location?: Location.Info
|
||||
}
|
||||
|
||||
export interface Interface {
|
||||
|
|
@ -264,7 +264,11 @@ export const layer = Layer.effect(
|
|||
const location =
|
||||
options?.location ??
|
||||
(serviceLocation
|
||||
? { directory: serviceLocation.directory, workspaceID: serviceLocation.workspaceID }
|
||||
? new Location.Info({
|
||||
directory: serviceLocation.directory,
|
||||
workspaceID: serviceLocation.workspaceID,
|
||||
project: serviceLocation.project,
|
||||
})
|
||||
: undefined)
|
||||
return yield* publishEvent({
|
||||
id: options?.id ?? ID.create(),
|
||||
|
|
|
|||
|
|
@ -10,16 +10,23 @@ export const Ref = Schema.Struct({
|
|||
}).annotate({ identifier: "Location.Ref" })
|
||||
export type Ref = typeof Ref.Type
|
||||
|
||||
export interface Interface {
|
||||
readonly directory: AbsolutePath
|
||||
readonly workspaceID?: string
|
||||
readonly project: {
|
||||
readonly id: Project.ID
|
||||
readonly directory: AbsolutePath
|
||||
}
|
||||
export class Info extends Schema.Class<Info>("Location.Info")({
|
||||
directory: AbsolutePath,
|
||||
workspaceID: Schema.String.pipe(Schema.optional),
|
||||
project: Schema.Struct({
|
||||
id: Project.ID,
|
||||
directory: AbsolutePath,
|
||||
}),
|
||||
}) {}
|
||||
|
||||
export interface Interface extends Info {
|
||||
readonly vcs?: Project.Vcs
|
||||
}
|
||||
|
||||
export function response<S extends Schema.Top>(data: S) {
|
||||
return Schema.Struct({ location: Info, data })
|
||||
}
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Location") {}
|
||||
|
||||
export const layer = (ref: Ref) =>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { Location } from "@opencode-ai/core/location"
|
|||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { Policy } from "@opencode-ai/core/policy"
|
||||
import { Project } from "@opencode-ai/core/project"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { location } from "./fixture/location"
|
||||
|
|
@ -187,7 +188,12 @@ describe("CatalogV2", () => {
|
|||
yield* events.publish(
|
||||
PluginV2.Event.Added,
|
||||
{ id: PluginV2.ID.make("test-transform") },
|
||||
{ location: { directory: AbsolutePath.make("other") } },
|
||||
{
|
||||
location: new Location.Info({
|
||||
directory: AbsolutePath.make("other"),
|
||||
project: { id: Project.ID.global, directory: AbsolutePath.make("other") },
|
||||
}),
|
||||
},
|
||||
)
|
||||
yield* Effect.yieldNow
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { Database } from "@opencode-ai/core/database/database"
|
|||
import { EventSequenceTable, EventTable } from "@opencode-ai/core/event/sql"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { Project } from "@opencode-ai/core/project"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { location } from "./fixture/location"
|
||||
import { testEffect } from "./lib/effect"
|
||||
|
|
@ -80,7 +81,11 @@ describe("EventV2", () => {
|
|||
expect(event.type).toBe("test.message")
|
||||
expect(event).not.toHaveProperty("version")
|
||||
expect(event.data).toEqual({ text: "hello" })
|
||||
expect(event.location).toEqual({ directory: AbsolutePath.make("project"), workspaceID: "workspace" })
|
||||
expect(event.location).toEqual({
|
||||
directory: AbsolutePath.make("project"),
|
||||
workspaceID: "workspace",
|
||||
project: { id: Project.ID.global, directory: AbsolutePath.make("project") },
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref"
|
||||
import { GlobalBus } from "@/bus/global"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { Project } from "@opencode-ai/core/project"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import "@opencode-ai/core/account"
|
||||
import "@opencode-ai/core/catalog"
|
||||
|
|
@ -24,10 +26,11 @@ export const layer = Layer.effect(
|
|||
const workspaceID = yield* WorkspaceRef
|
||||
return yield* events.publish(definition, data, {
|
||||
...options,
|
||||
location: {
|
||||
location: new Location.Info({
|
||||
directory: AbsolutePath.make(ctx.directory),
|
||||
...(workspaceID ? { workspaceID } : {}),
|
||||
},
|
||||
project: { id: Project.ID.make(ctx.project.id), directory: AbsolutePath.make(ctx.worktree) },
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { CommandV2 } from "@opencode-ai/core/command"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { Schema } from "effect"
|
||||
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { V2Authorization } from "../../middleware/authorization"
|
||||
|
|
@ -8,7 +9,7 @@ export const CommandGroup = HttpApiGroup.make("v2.command")
|
|||
.add(
|
||||
HttpApiEndpoint.get("commands", "/api/command", {
|
||||
query: LocationQuery,
|
||||
success: Schema.Array(CommandV2.Info),
|
||||
success: Location.response(Schema.Array(CommandV2.Info)),
|
||||
})
|
||||
.annotateMerge(locationQueryOpenApi)
|
||||
.annotateMerge(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { FileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { RelativePath } from "@opencode-ai/core/schema"
|
||||
import { Schema } from "effect"
|
||||
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
|
|
@ -21,7 +22,7 @@ export const FileSystemGroup = HttpApiGroup.make("v2.fs")
|
|||
.add(
|
||||
HttpApiEndpoint.get("read", "/api/fs/read", {
|
||||
query: ReadQuery,
|
||||
success: FileSystem.Content,
|
||||
success: Location.response(FileSystem.Content),
|
||||
})
|
||||
.annotateMerge(locationQueryOpenApi)
|
||||
.annotateMerge(
|
||||
|
|
@ -35,7 +36,7 @@ export const FileSystemGroup = HttpApiGroup.make("v2.fs")
|
|||
.add(
|
||||
HttpApiEndpoint.get("list", "/api/fs/list", {
|
||||
query: ListQuery,
|
||||
success: Schema.Array(FileSystem.Entry),
|
||||
success: Location.response(Schema.Array(FileSystem.Entry)),
|
||||
})
|
||||
.annotateMerge(locationQueryOpenApi)
|
||||
.annotateMerge(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { SkillV2 } from "@opencode-ai/core/skill"
|
|||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { PluginBoot } from "@opencode-ai/core/plugin/boot"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { HttpEffect, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
|
||||
import { HttpServerRequest } from "effect/unstable/http"
|
||||
import { HttpApiMiddleware, OpenApi } from "effect/unstable/httpapi"
|
||||
|
||||
export const LocationQuery = Schema.Struct({
|
||||
|
|
@ -36,6 +36,20 @@ export const locationQueryOpenApi = OpenApi.annotations({
|
|||
},
|
||||
})
|
||||
|
||||
export function response<A, E, R>(data: Effect.Effect<A, E, R>) {
|
||||
return Effect.gen(function* () {
|
||||
const location = yield* Location.Service
|
||||
return {
|
||||
location: new Location.Info({
|
||||
directory: location.directory,
|
||||
workspaceID: location.workspaceID,
|
||||
project: location.project,
|
||||
}),
|
||||
data: yield* data,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export class V2LocationMiddleware extends HttpApiMiddleware.Service<
|
||||
V2LocationMiddleware,
|
||||
{
|
||||
|
|
@ -67,19 +81,7 @@ export const layer = Layer.effect(
|
|||
return V2LocationMiddleware.of((effect) =>
|
||||
Effect.gen(function* () {
|
||||
const request = yield* HttpServerRequest.HttpServerRequest
|
||||
return yield* Effect.gen(function* () {
|
||||
const location = yield* Location.Service
|
||||
yield* HttpEffect.appendPreResponseHandler((_request, response) =>
|
||||
Effect.succeed(
|
||||
HttpServerResponse.setHeaders(response, {
|
||||
"x-opencode-directory": location.directory,
|
||||
"x-opencode-project-id": location.project.id,
|
||||
...(location.workspaceID ? { "x-opencode-workspace": location.workspaceID } : {}),
|
||||
}),
|
||||
),
|
||||
)
|
||||
return yield* effect
|
||||
}).pipe(Effect.provide(locations.get(ref(request))))
|
||||
return yield* effect.pipe(Effect.provide(locations.get(ref(request))))
|
||||
}),
|
||||
)
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { Schema } from "effect"
|
||||
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { ServiceUnavailableError } from "../../errors"
|
||||
|
|
@ -9,7 +10,7 @@ export const ModelGroup = HttpApiGroup.make("v2.model")
|
|||
.add(
|
||||
HttpApiEndpoint.get("models", "/api/model", {
|
||||
query: LocationQuery,
|
||||
success: Schema.Array(ModelV2.Info),
|
||||
success: Location.response(Schema.Array(ModelV2.Info)),
|
||||
error: ServiceUnavailableError,
|
||||
})
|
||||
.annotateMerge(locationQueryOpenApi)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { PermissionV2 } from "@opencode-ai/core/permission"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { PermissionSaved } from "@opencode-ai/core/permission/saved"
|
||||
import { ProjectV2 } from "@opencode-ai/core/project"
|
||||
import { SessionV2 } from "@opencode-ai/core/session"
|
||||
|
|
@ -12,7 +13,7 @@ export const PermissionGroup = HttpApiGroup.make("v2.permission")
|
|||
.add(
|
||||
HttpApiEndpoint.get("permissionRequests", "/api/permission/request", {
|
||||
query: LocationQuery,
|
||||
success: Schema.Array(PermissionV2.Request),
|
||||
success: Location.response(Schema.Array(PermissionV2.Request)),
|
||||
})
|
||||
.annotateMerge(locationQueryOpenApi)
|
||||
.annotateMerge(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { Schema } from "effect"
|
||||
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { ProviderNotFoundError, ServiceUnavailableError } from "../../errors"
|
||||
|
|
@ -9,7 +10,7 @@ export const ProviderGroup = HttpApiGroup.make("v2.provider")
|
|||
.add(
|
||||
HttpApiEndpoint.get("providers", "/api/provider", {
|
||||
query: LocationQuery,
|
||||
success: Schema.Array(ProviderV2.Info),
|
||||
success: Location.response(Schema.Array(ProviderV2.Info)),
|
||||
error: ServiceUnavailableError,
|
||||
})
|
||||
.annotateMerge(locationQueryOpenApi)
|
||||
|
|
@ -25,7 +26,7 @@ export const ProviderGroup = HttpApiGroup.make("v2.provider")
|
|||
HttpApiEndpoint.get("provider", "/api/provider/:providerID", {
|
||||
params: { providerID: ProviderV2.ID },
|
||||
query: LocationQuery,
|
||||
success: ProviderV2.Info,
|
||||
success: Location.response(ProviderV2.Info),
|
||||
error: [ProviderNotFoundError, ServiceUnavailableError],
|
||||
})
|
||||
.annotateMerge(locationQueryOpenApi)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { SkillV2 } from "@opencode-ai/core/skill"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { Schema } from "effect"
|
||||
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { V2Authorization } from "../../middleware/authorization"
|
||||
|
|
@ -8,7 +9,7 @@ export const SkillGroup = HttpApiGroup.make("v2.skill")
|
|||
.add(
|
||||
HttpApiEndpoint.get("skills", "/api/skill", {
|
||||
query: LocationQuery,
|
||||
success: Schema.Array(SkillV2.Info),
|
||||
success: Location.response(Schema.Array(SkillV2.Info)),
|
||||
})
|
||||
.annotateMerge(locationQueryOpenApi)
|
||||
.annotateMerge(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import { CommandV2 } from "@opencode-ai/core/command"
|
|||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
export const commandHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.command", (handlers) =>
|
||||
handlers.handle("commands", () => CommandV2.Service.use((command) => command.list())),
|
||||
handlers.handle("commands", () => response(CommandV2.Service.use((command) => command.list()))),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ import { FileSystem } from "@opencode-ai/core/filesystem"
|
|||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
export const fileSystemHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.fs", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
return handlers
|
||||
.handle("read", (ctx) => FileSystem.Service.use((fs) => fs.read(ctx.query)))
|
||||
.handle("list", (ctx) => FileSystem.Service.use((fs) => fs.list(ctx.query)))
|
||||
.handle("read", (ctx) => response(FileSystem.Service.use((fs) => fs.read(ctx.query))))
|
||||
.handle("list", (ctx) => response(FileSystem.Service.use((fs) => fs.list(ctx.query))))
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Effect } from "effect"
|
|||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { ServiceUnavailableError } from "../../errors"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
const catalogUnavailable = new ServiceUnavailableError({
|
||||
message: "Model catalog is unavailable",
|
||||
|
|
@ -18,7 +19,7 @@ export const modelHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.model", (
|
|||
const catalog = yield* Catalog.Service
|
||||
const pluginBoot = yield* PluginBoot.Service
|
||||
yield* pluginBoot.wait().pipe(Effect.catchDefect(() => Effect.fail(catalogUnavailable)))
|
||||
return yield* catalog.model.available()
|
||||
return yield* response(catalog.model.available())
|
||||
}),
|
||||
)
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { Effect } from "effect"
|
|||
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { PermissionNotFoundError, SessionNotFoundError } from "../../errors"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
function missingRequest(id: PermissionV2.ID) {
|
||||
return new PermissionNotFoundError({ requestID: id, message: `Permission request not found: ${id}` })
|
||||
|
|
@ -19,7 +20,7 @@ export const permissionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.perm
|
|||
return handlers.handle(
|
||||
"permissionRequests",
|
||||
Effect.fn(function* () {
|
||||
return yield* (yield* PermissionV2.Service).list()
|
||||
return yield* response((yield* PermissionV2.Service).list())
|
||||
}),
|
||||
)
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Effect } from "effect"
|
|||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { ProviderNotFoundError, ServiceUnavailableError } from "../../errors"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
const catalogUnavailable = new ServiceUnavailableError({
|
||||
message: "Provider catalog is unavailable",
|
||||
|
|
@ -19,7 +20,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.provid
|
|||
const catalog = yield* Catalog.Service
|
||||
const pluginBoot = yield* PluginBoot.Service
|
||||
yield* pluginBoot.wait().pipe(Effect.catchDefect(() => Effect.fail(catalogUnavailable)))
|
||||
return yield* catalog.provider.available()
|
||||
return yield* response(catalog.provider.available())
|
||||
}),
|
||||
)
|
||||
.handle(
|
||||
|
|
@ -28,7 +29,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.provid
|
|||
const catalog = yield* Catalog.Service
|
||||
const pluginBoot = yield* PluginBoot.Service
|
||||
yield* pluginBoot.wait().pipe(Effect.catchDefect(() => Effect.fail(catalogUnavailable)))
|
||||
return yield* catalog.provider.get(ctx.params.providerID).pipe(
|
||||
return yield* response(catalog.provider.get(ctx.params.providerID)).pipe(
|
||||
Effect.catchTag("CatalogV2.ProviderNotFound", (error) =>
|
||||
Effect.fail(
|
||||
new ProviderNotFoundError({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { SkillV2 } from "@opencode-ai/core/skill"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
export const skillHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.skill", (handlers) =>
|
||||
handlers.handle("skills", () => SkillV2.Service.use((skill) => skill.list())),
|
||||
handlers.handle("skills", () => response(SkillV2.Service.use((skill) => skill.list()))),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import type { Provider } from "@/provider/provider"
|
|||
import { Permission } from "@/permission"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { Effect, Layer, Option, Context, Schema, Types } from "effect"
|
||||
import { AbsolutePath, NonNegativeInt, optionalOmitUndefined } from "@opencode-ai/core/schema"
|
||||
import { NonNegativeInt, optionalOmitUndefined } from "@opencode-ai/core/schema"
|
||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
|
||||
|
|
@ -112,13 +112,6 @@ export function fromRow(row: SessionRow): Info {
|
|||
}
|
||||
}
|
||||
|
||||
function eventLocation(info: Pick<Info, "directory" | "workspaceID">) {
|
||||
return {
|
||||
directory: AbsolutePath.make(info.directory),
|
||||
workspaceID: info.workspaceID,
|
||||
}
|
||||
}
|
||||
|
||||
export function toRow(info: Info) {
|
||||
return {
|
||||
id: info.id,
|
||||
|
|
@ -544,20 +537,6 @@ export const layer: Layer.Layer<
|
|||
const events = yield* EventV2Bridge.Service
|
||||
const flags = yield* RuntimeFlags.Service
|
||||
|
||||
const locationForSession = Effect.fnUntraced(function* (sessionID: SessionID) {
|
||||
const row = yield* db
|
||||
.select({ directory: SessionTable.directory, workspaceID: SessionTable.workspace_id })
|
||||
.from(SessionTable)
|
||||
.where(eq(SessionTable.id, sessionID))
|
||||
.get()
|
||||
.pipe(Effect.orDie)
|
||||
if (!row) return
|
||||
return {
|
||||
directory: AbsolutePath.make(row.directory),
|
||||
workspaceID: row.workspaceID ?? undefined,
|
||||
}
|
||||
})
|
||||
|
||||
const createNext = Effect.fn("Session.createNext")(function* (input: {
|
||||
id?: SessionID
|
||||
title?: string
|
||||
|
|
@ -597,7 +576,6 @@ export const layer: Layer.Layer<
|
|||
yield* events.publish(
|
||||
SessionV1.Event.Created,
|
||||
{ sessionID: result.id, info: result },
|
||||
{ location: eventLocation(result) },
|
||||
)
|
||||
|
||||
return result
|
||||
|
|
@ -688,7 +666,6 @@ export const layer: Layer.Layer<
|
|||
yield* events.publish(
|
||||
SessionV1.Event.Deleted,
|
||||
{ sessionID, info: session },
|
||||
{ location: eventLocation(session) },
|
||||
)
|
||||
yield* events.remove(sessionID)
|
||||
} catch (e) {
|
||||
|
|
@ -698,14 +675,12 @@ export const layer: Layer.Layer<
|
|||
|
||||
const updateMessage = <T extends SessionV1.Info>(msg: T): Effect.Effect<T> =>
|
||||
Effect.gen(function* () {
|
||||
const location = yield* locationForSession(msg.sessionID)
|
||||
yield* events.publish(SessionV1.Event.MessageUpdated, { sessionID: msg.sessionID, info: msg }, { location })
|
||||
yield* events.publish(SessionV1.Event.MessageUpdated, { sessionID: msg.sessionID, info: msg })
|
||||
return msg
|
||||
}).pipe(Effect.withSpan("Session.updateMessage"))
|
||||
|
||||
const updatePart = <T extends SessionV1.Part>(part: T): Effect.Effect<T> =>
|
||||
Effect.gen(function* () {
|
||||
const location = yield* locationForSession(part.sessionID)
|
||||
yield* events.publish(
|
||||
SessionV1.Event.PartUpdated,
|
||||
{
|
||||
|
|
@ -713,7 +688,6 @@ export const layer: Layer.Layer<
|
|||
part: structuredClone(part),
|
||||
time: Date.now(),
|
||||
},
|
||||
{ location },
|
||||
)
|
||||
return part
|
||||
}).pipe(Effect.withSpan("Session.updatePart"))
|
||||
|
|
@ -819,7 +793,7 @@ export const layer: Layer.Layer<
|
|||
revert: info.revert === null ? undefined : (info.revert ?? current.revert),
|
||||
permission: info.permission === null ? undefined : (info.permission ?? current.permission),
|
||||
} as Info
|
||||
yield* events.publish(SessionV1.Event.Updated, { sessionID, info: next }, { location: eventLocation(next) })
|
||||
yield* events.publish(SessionV1.Event.Updated, { sessionID, info: next })
|
||||
})
|
||||
|
||||
const touch = Effect.fn("Session.touch")(function* (sessionID: SessionID) {
|
||||
|
|
@ -917,14 +891,12 @@ export const layer: Layer.Layer<
|
|||
sessionID: SessionID
|
||||
messageID: MessageID
|
||||
}) {
|
||||
const location = yield* locationForSession(input.sessionID)
|
||||
yield* events.publish(
|
||||
SessionV1.Event.MessageRemoved,
|
||||
{
|
||||
sessionID: input.sessionID,
|
||||
messageID: input.messageID,
|
||||
},
|
||||
{ location },
|
||||
)
|
||||
return input.messageID
|
||||
})
|
||||
|
|
@ -934,7 +906,6 @@ export const layer: Layer.Layer<
|
|||
messageID: MessageID
|
||||
partID: PartID
|
||||
}) {
|
||||
const location = yield* locationForSession(input.sessionID)
|
||||
yield* events.publish(
|
||||
SessionV1.Event.PartRemoved,
|
||||
{
|
||||
|
|
@ -942,7 +913,6 @@ export const layer: Layer.Layer<
|
|||
messageID: input.messageID,
|
||||
partID: input.partID,
|
||||
},
|
||||
{ location },
|
||||
)
|
||||
return input.partID
|
||||
})
|
||||
|
|
|
|||
|
|
@ -26,15 +26,16 @@ afterEach(async () => {
|
|||
})
|
||||
|
||||
describe("v2 location HttpApi", () => {
|
||||
test("returns command and skill snapshots with resolved location headers", async () => {
|
||||
test("returns command and skill snapshots with resolved locations", async () => {
|
||||
await using tmp = await tmpdir({ git: true })
|
||||
|
||||
for (const route of ["/api/command", "/api/skill"]) {
|
||||
const response = await request(route, tmp.path)
|
||||
expect(response.status).toBe(200)
|
||||
expect(await response.json()).toBeArray()
|
||||
expect(response.headers.get("x-opencode-directory")).toBe(tmp.path)
|
||||
expect(response.headers.get("x-opencode-project-id")).toBeTruthy()
|
||||
const body = (await response.json()) as { location: { directory: string; project: { id: string } }; data: unknown }
|
||||
expect(body.data).toBeArray()
|
||||
expect(body.location.directory).toBe(tmp.path)
|
||||
expect(body.location.project.id).toBeTruthy()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue