location.ts 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import { Location } from "@opencode-ai/core/location"
  2. import { LocationServiceMap } from "@opencode-ai/core/location-layer"
  3. import { FileSystem } from "@opencode-ai/core/filesystem"
  4. import { AbsolutePath } from "@opencode-ai/core/schema"
  5. import { WorkspaceV2 } from "@opencode-ai/core/workspace"
  6. import { Effect, Layer, Schema } from "effect"
  7. import { HttpServerRequest } from "effect/unstable/http"
  8. import { HttpApiMiddleware, OpenApi } from "effect/unstable/httpapi"
  9. export const LocationQuery = Schema.Struct({
  10. location: Schema.optional(
  11. Schema.Struct({
  12. directory: Schema.optional(Schema.String),
  13. workspace: Schema.optional(Schema.String),
  14. }),
  15. ),
  16. }).annotate({ identifier: "LocationQuery" })
  17. export const locationQueryOpenApi = OpenApi.annotations({
  18. transform: (operation) => {
  19. const parameters = operation.parameters
  20. if (!Array.isArray(parameters)) return operation
  21. return {
  22. ...operation,
  23. parameters: parameters.map((parameter) =>
  24. parameter?.name === "location" && parameter?.in === "query"
  25. ? { ...parameter, style: "deepObject", explode: true }
  26. : parameter,
  27. ),
  28. }
  29. },
  30. })
  31. export function response<A, E, R>(data: Effect.Effect<A, E, R>) {
  32. return Effect.gen(function* () {
  33. const location = yield* Location.Service
  34. return {
  35. location: new Location.Info({
  36. directory: location.directory,
  37. workspaceID: location.workspaceID,
  38. project: location.project,
  39. }),
  40. data: yield* data,
  41. }
  42. })
  43. }
  44. export type LocationServices = Layer.Success<ReturnType<typeof LocationServiceMap.get>>
  45. export class LocationMiddleware extends HttpApiMiddleware.Service<
  46. LocationMiddleware,
  47. {
  48. provides: LocationServices
  49. }
  50. >()("@opencode/HttpApiLocation") {}
  51. function ref(request: HttpServerRequest.HttpServerRequest): Location.Ref {
  52. const query = new URL(request.url, "http://localhost").searchParams
  53. const workspaceID = query.get("location[workspace]") || request.headers["x-opencode-workspace"]
  54. return Location.Ref.make({
  55. directory: AbsolutePath.make(
  56. query.get("location[directory]") || request.headers["x-opencode-directory"] || process.cwd(),
  57. ),
  58. workspaceID: workspaceID ? WorkspaceV2.ID.make(workspaceID) : undefined,
  59. })
  60. }
  61. export const layer = Layer.effect(
  62. LocationMiddleware,
  63. Effect.gen(function* () {
  64. const locations = yield* LocationServiceMap
  65. return LocationMiddleware.of((effect) =>
  66. Effect.gen(function* () {
  67. const request = yield* HttpServerRequest.HttpServerRequest
  68. return yield* effect.pipe(Effect.provide(locations.get(ref(request))))
  69. }),
  70. )
  71. }),
  72. )