integration.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import { Integration } from "@opencode-ai/core/integration"
  2. import { Effect } from "effect"
  3. import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
  4. import { Api } from "../api"
  5. import { InvalidRequestError } from "../errors"
  6. import { response } from "../groups/location"
  7. const authorize = <A, R>(effect: Effect.Effect<A, Integration.AuthorizationError, R>) =>
  8. effect.pipe(
  9. Effect.mapError(
  10. () =>
  11. new InvalidRequestError({
  12. message: "Authentication failed",
  13. kind: "integration_authorization",
  14. }),
  15. ),
  16. )
  17. export const IntegrationHandler = HttpApiBuilder.group(Api, "server.integration", (handlers) =>
  18. Effect.gen(function* () {
  19. return handlers
  20. .handle(
  21. "integration.list",
  22. Effect.fn(function* () {
  23. const service = yield* Integration.Service
  24. return yield* response(service.list())
  25. }),
  26. )
  27. .handle(
  28. "integration.get",
  29. Effect.fn(function* (ctx) {
  30. const service = yield* Integration.Service
  31. return yield* response(service.get(ctx.params.integrationID))
  32. }),
  33. )
  34. .handle(
  35. "integration.connect.key",
  36. Effect.fn(function* (ctx) {
  37. const service = yield* Integration.Service
  38. yield* authorize(
  39. service.connection.key({
  40. integrationID: ctx.params.integrationID,
  41. key: ctx.payload.key,
  42. label: ctx.payload.label,
  43. }),
  44. )
  45. return HttpApiSchema.NoContent.make()
  46. }),
  47. )
  48. .handle(
  49. "integration.connect.oauth",
  50. Effect.fn(function* (ctx) {
  51. const service = yield* Integration.Service
  52. return yield* response(
  53. authorize(
  54. service.connection.oauth({
  55. integrationID: ctx.params.integrationID,
  56. methodID: ctx.payload.methodID,
  57. inputs: ctx.payload.inputs,
  58. label: ctx.payload.label,
  59. }),
  60. ),
  61. )
  62. }),
  63. )
  64. .handle(
  65. "integration.attempt.status",
  66. Effect.fn(function* (ctx) {
  67. const service = yield* Integration.Service
  68. return yield* response(service.attempt.status(ctx.params.attemptID))
  69. }),
  70. )
  71. .handle(
  72. "integration.attempt.complete",
  73. Effect.fn(function* (ctx) {
  74. const service = yield* Integration.Service
  75. yield* service.attempt.complete({ attemptID: ctx.params.attemptID, code: ctx.payload.code }).pipe(
  76. Effect.mapError(
  77. (error) =>
  78. new InvalidRequestError({
  79. message:
  80. error._tag === "Integration.CodeRequired"
  81. ? "Authorization code is required"
  82. : "Authentication failed",
  83. kind:
  84. error._tag === "Integration.CodeRequired"
  85. ? "integration_code_required"
  86. : "integration_authorization",
  87. }),
  88. ),
  89. )
  90. return HttpApiSchema.NoContent.make()
  91. }),
  92. )
  93. .handle(
  94. "integration.attempt.cancel",
  95. Effect.fn(function* (ctx) {
  96. const service = yield* Integration.Service
  97. yield* service.attempt.cancel(ctx.params.attemptID)
  98. return HttpApiSchema.NoContent.make()
  99. }),
  100. )
  101. }),
  102. )