tui-sdk.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import type { GlobalEvent } from "@opencode-ai/sdk/v2"
  2. import type { EventSource } from "../../src/context/sdk"
  3. export const worktree = "/tmp/opencode"
  4. export const directory = `${worktree}/packages/tui`
  5. export function json(data: unknown, init?: ResponseInit) {
  6. return new Response(JSON.stringify(data), {
  7. ...init,
  8. headers: { "content-type": "application/json", ...(init?.headers ?? {}) },
  9. })
  10. }
  11. export function eventSource(): EventSource {
  12. return { subscribe: async () => () => {} }
  13. }
  14. export function createEventSource() {
  15. let fn: ((event: GlobalEvent) => void) | undefined
  16. let stream: ReadableStreamDefaultController<Uint8Array> | undefined
  17. const pending: Uint8Array[] = []
  18. return {
  19. source: {
  20. subscribe: async (handler: (event: GlobalEvent) => void) => {
  21. fn = handler
  22. return () => {
  23. if (fn === handler) fn = undefined
  24. }
  25. },
  26. } satisfies EventSource,
  27. emit(event: GlobalEvent) {
  28. if (!fn) throw new Error("event source not ready")
  29. fn(event)
  30. if (!("properties" in event.payload)) return
  31. const chunk = new TextEncoder().encode(
  32. `data: ${JSON.stringify({
  33. ...event.payload,
  34. location: { directory: event.directory, workspaceID: event.workspace },
  35. data: event.payload.properties,
  36. })}\n\n`,
  37. )
  38. if (stream) return stream.enqueue(chunk)
  39. pending.push(chunk)
  40. },
  41. response() {
  42. return new Response(
  43. new ReadableStream<Uint8Array>({
  44. start(controller) {
  45. stream = controller
  46. for (const chunk of pending.splice(0)) controller.enqueue(chunk)
  47. },
  48. cancel() {
  49. stream = undefined
  50. },
  51. }),
  52. { headers: { "content-type": "text/event-stream" } },
  53. )
  54. },
  55. }
  56. }
  57. export type FetchHandler = (url: URL) => Response | Promise<Response> | undefined
  58. export function createFetch(override?: FetchHandler, events?: ReturnType<typeof createEventSource>) {
  59. const session = [] as URL[]
  60. const fetch = (async (input: RequestInfo | URL) => {
  61. const url = new URL(input instanceof Request ? input.url : String(input))
  62. if (url.pathname === "/session") session.push(url)
  63. const overridden = await override?.(url)
  64. if (overridden) return overridden
  65. if (url.pathname === "/api/event" && events) return events.response()
  66. if (
  67. [
  68. "/agent",
  69. "/command",
  70. "/experimental/workspace",
  71. "/experimental/workspace/status",
  72. "/formatter",
  73. "/lsp",
  74. ].includes(url.pathname)
  75. )
  76. return json([])
  77. if (["/config", "/experimental/resource", "/mcp", "/provider/auth", "/session/status"].includes(url.pathname))
  78. return json({})
  79. if (url.pathname === "/config/providers") return json({ providers: {}, default: {} })
  80. if (url.pathname === "/experimental/console") return json({ consoleManagedProviders: [], switchableOrgCount: 0 })
  81. if (url.pathname === "/experimental/capabilities") return json({ backgroundSubagents: false })
  82. if (url.pathname === "/path") return json({ home: "", state: "", config: "", worktree, directory })
  83. if (url.pathname === "/api/location") return json({ directory, project: { id: "proj_test", directory: worktree } })
  84. if (
  85. ["/api/agent", "/api/model", "/api/provider", "/api/integration", "/api/command", "/api/skill"].includes(
  86. url.pathname,
  87. )
  88. )
  89. return json({
  90. location: { directory, project: { id: "proj_test", directory: worktree } },
  91. data: [],
  92. })
  93. if (url.pathname === "/project/current") return json({ id: "proj_test" })
  94. if (url.pathname === "/api/reference")
  95. return json({ location: { directory, project: { id: "proj_test", directory } }, data: [] })
  96. if (url.pathname === "/provider") return json({ all: [], default: {}, connected: [] })
  97. if (url.pathname === "/session") return json([])
  98. if (url.pathname === "/vcs") return json({ branch: "main" })
  99. throw new Error(`unexpected request: ${url.pathname}`)
  100. }) as typeof globalThis.fetch
  101. return { fetch, session }
  102. }