index.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. import { describeRoute, resolver, validator } from "hono-openapi"
  2. import { Hono } from "hono"
  3. import type { UpgradeWebSocket } from "hono/ws"
  4. import { Effect } from "effect"
  5. import z from "zod"
  6. import { Format } from "../../format"
  7. import { TuiRoutes } from "./tui"
  8. import { Instance } from "../../project/instance"
  9. import { Vcs } from "../../project/vcs"
  10. import { Agent } from "../../agent/agent"
  11. import { Skill } from "../../skill"
  12. import { Global } from "../../global"
  13. import { LSP } from "../../lsp"
  14. import { Command } from "../../command"
  15. import { QuestionRoutes } from "./question"
  16. import { PermissionRoutes } from "./permission"
  17. import { Flag } from "@/flag/flag"
  18. import { ExperimentalHttpApiServer } from "./httpapi/server"
  19. import { ProjectRoutes } from "./project"
  20. import { SessionRoutes } from "./session"
  21. import { PtyRoutes } from "./pty"
  22. import { McpRoutes } from "./mcp"
  23. import { FileRoutes } from "./file"
  24. import { ConfigRoutes } from "./config"
  25. import { ExperimentalRoutes } from "./experimental"
  26. import { ProviderRoutes } from "./provider"
  27. import { EventRoutes } from "./event"
  28. import { SyncRoutes } from "./sync"
  29. import { WorkspaceRouterMiddleware } from "./middleware"
  30. import { AppRuntime } from "@/effect/app-runtime"
  31. export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
  32. const app = new Hono()
  33. .use(WorkspaceRouterMiddleware(upgrade))
  34. .route("/project", ProjectRoutes())
  35. .route("/pty", PtyRoutes(upgrade))
  36. .route("/config", ConfigRoutes())
  37. .route("/experimental", ExperimentalRoutes())
  38. .route("/session", SessionRoutes())
  39. .route("/permission", PermissionRoutes())
  40. if (Flag.OPENCODE_EXPERIMENTAL_HTTPAPI) {
  41. const handler = ExperimentalHttpApiServer.webHandler().handler
  42. app.all("/question", (c) => handler(c.req.raw)).all("/question/*", (c) => handler(c.req.raw))
  43. }
  44. return app
  45. .route("/question", QuestionRoutes())
  46. .route("/provider", ProviderRoutes())
  47. .route("/sync", SyncRoutes())
  48. .route("/", FileRoutes())
  49. .route("/", EventRoutes())
  50. .route("/mcp", McpRoutes())
  51. .route("/tui", TuiRoutes())
  52. .post(
  53. "/instance/dispose",
  54. describeRoute({
  55. summary: "Dispose instance",
  56. description: "Clean up and dispose the current OpenCode instance, releasing all resources.",
  57. operationId: "instance.dispose",
  58. responses: {
  59. 200: {
  60. description: "Instance disposed",
  61. content: {
  62. "application/json": {
  63. schema: resolver(z.boolean()),
  64. },
  65. },
  66. },
  67. },
  68. }),
  69. async (c) => {
  70. await Instance.dispose()
  71. return c.json(true)
  72. },
  73. )
  74. .get(
  75. "/path",
  76. describeRoute({
  77. summary: "Get paths",
  78. description: "Retrieve the current working directory and related path information for the OpenCode instance.",
  79. operationId: "path.get",
  80. responses: {
  81. 200: {
  82. description: "Path",
  83. content: {
  84. "application/json": {
  85. schema: resolver(
  86. z
  87. .object({
  88. home: z.string(),
  89. state: z.string(),
  90. config: z.string(),
  91. worktree: z.string(),
  92. directory: z.string(),
  93. })
  94. .meta({
  95. ref: "Path",
  96. }),
  97. ),
  98. },
  99. },
  100. },
  101. },
  102. }),
  103. async (c) => {
  104. return c.json({
  105. home: Global.Path.home,
  106. state: Global.Path.state,
  107. config: Global.Path.config,
  108. worktree: Instance.worktree,
  109. directory: Instance.directory,
  110. })
  111. },
  112. )
  113. .get(
  114. "/vcs",
  115. describeRoute({
  116. summary: "Get VCS info",
  117. description: "Retrieve version control system (VCS) information for the current project, such as git branch.",
  118. operationId: "vcs.get",
  119. responses: {
  120. 200: {
  121. description: "VCS info",
  122. content: {
  123. "application/json": {
  124. schema: resolver(Vcs.Info),
  125. },
  126. },
  127. },
  128. },
  129. }),
  130. async (c) => {
  131. return c.json(
  132. await AppRuntime.runPromise(
  133. Effect.gen(function* () {
  134. const vcs = yield* Vcs.Service
  135. const [branch, default_branch] = yield* Effect.all([vcs.branch(), vcs.defaultBranch()], {
  136. concurrency: 2,
  137. })
  138. return { branch, default_branch }
  139. }),
  140. ),
  141. )
  142. },
  143. )
  144. .get(
  145. "/vcs/diff",
  146. describeRoute({
  147. summary: "Get VCS diff",
  148. description: "Retrieve the current git diff for the working tree or against the default branch.",
  149. operationId: "vcs.diff",
  150. responses: {
  151. 200: {
  152. description: "VCS diff",
  153. content: {
  154. "application/json": {
  155. schema: resolver(Vcs.FileDiff.array()),
  156. },
  157. },
  158. },
  159. },
  160. }),
  161. validator(
  162. "query",
  163. z.object({
  164. mode: Vcs.Mode,
  165. }),
  166. ),
  167. async (c) => {
  168. return c.json(
  169. await AppRuntime.runPromise(
  170. Effect.gen(function* () {
  171. const vcs = yield* Vcs.Service
  172. return yield* vcs.diff(c.req.valid("query").mode)
  173. }),
  174. ),
  175. )
  176. },
  177. )
  178. .get(
  179. "/command",
  180. describeRoute({
  181. summary: "List commands",
  182. description: "Get a list of all available commands in the OpenCode system.",
  183. operationId: "command.list",
  184. responses: {
  185. 200: {
  186. description: "List of commands",
  187. content: {
  188. "application/json": {
  189. schema: resolver(Command.Info.array()),
  190. },
  191. },
  192. },
  193. },
  194. }),
  195. async (c) => {
  196. const commands = await AppRuntime.runPromise(Command.Service.use((svc) => svc.list()))
  197. return c.json(commands)
  198. },
  199. )
  200. .get(
  201. "/agent",
  202. describeRoute({
  203. summary: "List agents",
  204. description: "Get a list of all available AI agents in the OpenCode system.",
  205. operationId: "app.agents",
  206. responses: {
  207. 200: {
  208. description: "List of agents",
  209. content: {
  210. "application/json": {
  211. schema: resolver(Agent.Info.array()),
  212. },
  213. },
  214. },
  215. },
  216. }),
  217. async (c) => {
  218. const modes = await AppRuntime.runPromise(Agent.Service.use((svc) => svc.list()))
  219. return c.json(modes)
  220. },
  221. )
  222. .get(
  223. "/skill",
  224. describeRoute({
  225. summary: "List skills",
  226. description: "Get a list of all available skills in the OpenCode system.",
  227. operationId: "app.skills",
  228. responses: {
  229. 200: {
  230. description: "List of skills",
  231. content: {
  232. "application/json": {
  233. schema: resolver(Skill.Info.array()),
  234. },
  235. },
  236. },
  237. },
  238. }),
  239. async (c) => {
  240. const skills = await AppRuntime.runPromise(
  241. Effect.gen(function* () {
  242. const skill = yield* Skill.Service
  243. return yield* skill.all()
  244. }),
  245. )
  246. return c.json(skills)
  247. },
  248. )
  249. .get(
  250. "/lsp",
  251. describeRoute({
  252. summary: "Get LSP status",
  253. description: "Get LSP server status",
  254. operationId: "lsp.status",
  255. responses: {
  256. 200: {
  257. description: "LSP server status",
  258. content: {
  259. "application/json": {
  260. schema: resolver(LSP.Status.array()),
  261. },
  262. },
  263. },
  264. },
  265. }),
  266. async (c) => {
  267. const items = await AppRuntime.runPromise(LSP.Service.use((lsp) => lsp.status()))
  268. return c.json(items)
  269. },
  270. )
  271. .get(
  272. "/formatter",
  273. describeRoute({
  274. summary: "Get formatter status",
  275. description: "Get formatter status",
  276. operationId: "formatter.status",
  277. responses: {
  278. 200: {
  279. description: "Formatter status",
  280. content: {
  281. "application/json": {
  282. schema: resolver(Format.Status.array()),
  283. },
  284. },
  285. },
  286. },
  287. }),
  288. async (c) => {
  289. return c.json(await AppRuntime.runPromise(Format.Service.use((svc) => svc.status())))
  290. },
  291. )
  292. }