feat(core): include resolved location info

This commit is contained in:
Dax Raad 2026-06-03 17:47:38 -04:00
commit 0bd61d2826
20 changed files with 92 additions and 82 deletions

View file

@ -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(),

View file

@ -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) =>

View file

@ -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

View file

@ -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") },
})
}),
)