git.iko.soy/opencode-android
on f9ed438 help sign in
110 lines 3.8 kB history raw
1package soy.iko.opencode.data.network
3import soy.iko.opencode.data.model.CreateSessionRequest
4import soy.iko.opencode.data.model.FileContent
5import soy.iko.opencode.data.model.FileNode
6import soy.iko.opencode.data.model.FileStatusEntry
7import soy.iko.opencode.data.model.MessageWithParts
8import soy.iko.opencode.data.model.ModelRef
9import soy.iko.opencode.data.model.PermissionReplyBody
10import soy.iko.opencode.data.model.PermissionResponse
11import soy.iko.opencode.data.model.PromptPart
12import soy.iko.opencode.data.model.PromptRequest
13import soy.iko.opencode.data.model.ProvidersResponse
14import soy.iko.opencode.data.model.Session
15import soy.iko.opencode.data.model.UpdateSessionRequest
16import io.ktor.client.HttpClient
17import io.ktor.client.call.body
18import io.ktor.client.request.delete
19import io.ktor.client.request.get
20import io.ktor.client.request.parameter
21import io.ktor.client.request.patch
22import io.ktor.client.request.post
23import io.ktor.client.request.setBody
24import io.ktor.http.ContentType
25import io.ktor.http.contentType
27/**
28 * Request/response wrapper over the opencode REST endpoints. The long-lived `/event`
29 * SSE stream lives in [EventStreamClient]; everything else is here.
30 */
31class OpencodeApiClient(private val client: HttpClient) {
33 /** Lightweight connectivity check used by the connect screen. */
34 suspend fun ping(): Boolean {
35 client.get("app")
36 return true
37 }
39 suspend fun listSessions(): List<Session> =
40 client.get("session").body()
42 suspend fun createSession(title: String? = null): Session =
43 client.post("session") {
44 contentType(ContentType.Application.Json)
45 setBody(CreateSessionRequest(title = title))
46 }.body()
48 suspend fun updateSession(id: String, title: String): Session =
49 client.patch("session/$id") {
50 contentType(ContentType.Application.Json)
51 setBody(UpdateSessionRequest(title = title))
52 }.body()
54 suspend fun deleteSession(id: String) {
55 client.delete("session/$id")
56 }
58 suspend fun listMessages(sessionId: String): List<MessageWithParts> =
59 client.get("session/$sessionId/message").body()
61 suspend fun sendPrompt(
62 sessionId: String,
63 text: String,
64 model: ModelRef? = null,
65 agent: String? = null,
66 ): MessageWithParts =
67 client.post("session/$sessionId/message") {
68 contentType(ContentType.Application.Json)
69 setBody(
70 PromptRequest(
71 parts = listOf(PromptPart(text = text)),
72 model = model,
73 agent = agent,
74 ),
75 )
76 }.body()
78 suspend fun abort(sessionId: String) {
79 client.post("session/$sessionId/abort")
80 }
82 suspend fun providers(): ProvidersResponse =
83 client.get("config/providers").body()
85 /** Respond to a permission request so a paused tool run can proceed. */
86 suspend fun respondPermission(
87 sessionId: String,
88 permissionId: String,
89 response: PermissionResponse,
90 ) {
91 client.post("session/$sessionId/permissions/$permissionId") {
92 contentType(ContentType.Application.Json)
93 setBody(PermissionReplyBody(response.wire))
94 }
95 }
97 // --- Files ---
99 suspend fun findFiles(query: String): List<String> =
100 client.get("find/file") { parameter("query", query) }.body()
102 suspend fun listDirectory(path: String): List<FileNode> =
103 client.get("file") { parameter("path", path) }.body()
105 suspend fun readFile(path: String): FileContent =
106 client.get("file/content") { parameter("path", path) }.body()
108 suspend fun fileStatus(): List<FileStatusEntry> =
109 client.get("file/status").body()