@@ -3,6 +3,7 @@ package com.ashotn.opencode.companion.api.session
33import com.ashotn.opencode.companion.api.transport.ApiError
44import com.ashotn.opencode.companion.api.transport.ApiResult
55import com.ashotn.opencode.companion.api.transport.OpenCodeHttpTransport
6+ import com.ashotn.opencode.companion.api.transport.parseBooleanResponse
67import com.ashotn.opencode.companion.api.transport.mapJsonArrayResponse
78import com.ashotn.opencode.companion.api.transport.mapJsonObjectResponse
89import com.ashotn.opencode.companion.api.transport.withParseContext
@@ -26,19 +27,9 @@ class SessionApiClient(
2627 val after : String? ,
2728 )
2829
29- data class HierarchySnapshot (
30- val sessionIds : Set <String >,
31- val parentBySessionId : Map <String , String >,
32- val titleBySessionId : Map <String , String >,
33- val descriptionBySessionId : Map <String , String >,
34- val updatedAtBySessionId : Map <String , Long >,
35- /* * Session IDs that have at least one message (server returned a non-null summary). */
36- val sessionIdsWithMessages : Set <String >,
37- )
38-
3930 fun createSession (port : Int ): ApiResult <CreatedSession > {
4031 val endpoint = SessionEndpoints .create()
41- val response = transport.postJson (port = port, path = endpoint.path, payload = JsonObject ().toString())
32+ val response = transport.post (port = port, path = endpoint.path, payload = JsonObject ().toString())
4233 return transport.mapJsonObjectResponse(response) { sessionObj ->
4334 val id = sessionObj.getStringOrNull(" id" )
4435 if (id.isNullOrBlank()) {
@@ -86,7 +77,12 @@ class SessionApiClient(
8677 }
8778 }
8879
89- fun fetchFileDiffPreview (port : Int , sessionId : String , projectBase : String , absFilePath : String ): ApiResult <FileDiffPreview ?> {
80+ fun fetchFileDiffPreview (
81+ port : Int ,
82+ sessionId : String ,
83+ projectBase : String ,
84+ absFilePath : String
85+ ): ApiResult <FileDiffPreview ?> {
9086 return when (val snapshot = fetchSessionDiffSnapshot(port, sessionId)) {
9187 is ApiResult .Failure -> snapshot
9288 is ApiResult .Success -> {
@@ -99,66 +95,107 @@ class SessionApiClient(
9995 }
10096 }
10197
102- fun fetchSessionHierarchy (port : Int ): ApiResult <HierarchySnapshot > {
98+ fun fetchSessionHierarchy (port : Int ): ApiResult <List < Session > > {
10399 val endpoint = SessionEndpoints .list()
104100 val response = transport.get(port = port, path = endpoint.path)
105101 return transport.mapJsonArrayResponse(response) { sessionArray ->
106- val sessionIds = linkedSetOf<String >()
107- val parentByChild = HashMap <String , String >()
108- val titleBySession = HashMap <String , String >()
109- val descriptionBySession = HashMap <String , String >()
110- val updatedAtBySession = HashMap <String , Long >()
111- val sessionIdsWithMessages = linkedSetOf<String >()
112-
113- sessionArray.forEach { element ->
114- if (! element.isJsonObject) return @forEach
115- val sessionObj = element.asJsonObject
116- val id = sessionObj.getStringOrNull(" id" )
117- ? : sessionObj.getStringOrNull(" sessionID" )
118- ? : return @forEach
119- sessionIds.add(id)
120-
121- val title = sessionObj.getStringOrNull(" title" )
122- if (! title.isNullOrBlank()) {
123- titleBySession[id] = title
124- }
125-
126- val description = sessionObj.getStringOrNull(" description" )
127- if (! description.isNullOrBlank()) {
128- descriptionBySession[id] = description
129- }
130-
131- val parent = sessionObj.getStringOrNull(" parentID" )
132- if (! parent.isNullOrBlank()) {
133- parentByChild[id] = parent
102+ val sessions = mutableListOf<Session >()
103+ for (element in sessionArray) {
104+ if (! element.isJsonObject) continue
105+ when (val result = parseSession(element.asJsonObject)) {
106+ is ApiResult .Failure -> return @mapJsonArrayResponse result
107+ is ApiResult .Success -> sessions.add(result.value)
134108 }
109+ }
110+ ApiResult .Success (sessions)
111+ }.withParseContext(endpoint)
112+ }
135113
136- val timeObj = sessionObj.getObjectOrNull(" time" )
137- val updatedAt = timeObj?.get(" updated" )
138- ?.takeIf { it.isJsonPrimitive }
139- ?.let { runCatching { it.asLong }.getOrNull() }
140- if (updatedAt != null && updatedAt > 0L ) {
141- updatedAtBySession[id] = updatedAt
142- }
114+ private fun parseSession (obj : JsonObject ): ApiResult <Session > {
115+ val id = obj.getStringOrNull(" id" )
116+ if (id.isNullOrBlank()) {
117+ return ApiResult .Failure (ApiError .ParseError (" Session is missing id" ))
118+ }
143119
144- // A session has messages if the server returns a non-null summary object.
145- // Brand-new sessions with no messages have no summary field at all.
146- if (sessionObj.getObjectOrNull(" summary" ) != null ) {
147- sessionIdsWithMessages.add(id)
148- }
120+ val projectID = obj.getStringOrNull(" projectID" )
121+ val directory = obj.getStringOrNull(" directory" )
122+ val parentID = obj.getStringOrNull(" parentID" )
123+ val title = obj.getStringOrNull(" title" )
124+ val version = obj.getStringOrNull(" version" )
125+
126+ val timeObj = obj.getObjectOrNull(" time" )
127+ val timeCreated = timeObj?.get(" created" )
128+ ?.takeIf { it.isJsonPrimitive }
129+ ?.let { runCatching { it.asLong }.getOrNull() } ? : 0L
130+ val timeUpdated = timeObj?.get(" updated" )
131+ ?.takeIf { it.isJsonPrimitive }
132+ ?.let { runCatching { it.asLong }.getOrNull() } ? : 0L
133+ val timeCompacting = timeObj?.get(" compacting" )
134+ ?.takeIf { it.isJsonPrimitive }
135+ ?.let { runCatching { it.asLong }.getOrNull() }
136+ val sessionTime = SessionTime (created = timeCreated, updated = timeUpdated, compacting = timeCompacting)
137+
138+ val summaryObj = obj.getObjectOrNull(" summary" )
139+ val summary = if (summaryObj != null ) {
140+ val additions = summaryObj.getIntOrNull(" additions" ) ? : 0
141+ val deletions = summaryObj.getIntOrNull(" deletions" ) ? : 0
142+ val files = summaryObj.getIntOrNull(" files" ) ? : 0
143+ val diffsArray = summaryObj.get(" diffs" )
144+ ?.takeIf { it.isJsonArray }
145+ ?.asJsonArray
146+ val diffs = diffsArray?.mapNotNull { diffElement ->
147+ if (! diffElement.isJsonObject) return @mapNotNull null
148+ val diffObj = diffElement.asJsonObject
149+ val file = diffObj.getStringOrNull(" file" ) ? : return @mapNotNull null
150+ FileDiff (
151+ file = file,
152+ before = diffObj.getStringOrNull(" before" ) ? : " " ,
153+ after = diffObj.getStringOrNull(" after" ) ? : " " ,
154+ additions = diffObj.getIntOrNull(" additions" ) ? : 0 ,
155+ deletions = diffObj.getIntOrNull(" deletions" ) ? : 0 ,
156+ )
149157 }
158+ SessionSummary (additions = additions, deletions = deletions, files = files, diffs = diffs)
159+ } else {
160+ null
161+ }
150162
151- ApiResult .Success (
152- HierarchySnapshot (
153- sessionIds = sessionIds,
154- parentBySessionId = parentByChild,
155- titleBySessionId = titleBySession,
156- descriptionBySessionId = descriptionBySession,
157- updatedAtBySessionId = updatedAtBySession,
158- sessionIdsWithMessages = sessionIdsWithMessages,
159- )
163+ val shareObj = obj.getObjectOrNull(" share" )
164+ val share = if (shareObj != null ) {
165+ SessionShare (url = shareObj.getStringOrNull(" url" ))
166+ } else {
167+ null
168+ }
169+
170+ return ApiResult .Success (
171+ Session (
172+ id = id,
173+ projectID = projectID,
174+ directory = directory,
175+ parentID = parentID,
176+ title = title,
177+ version = version,
178+ time = sessionTime,
179+ summary = summary,
180+ share = share,
160181 )
161- }.withParseContext(endpoint)
182+ )
183+ }
184+
185+ fun deleteSession (port : Int , sessionId : String ): ApiResult <Boolean > {
186+ val endpoint = SessionEndpoints .delete(sessionId)
187+ val response = transport.delete(port = port, path = endpoint.path)
188+ return transport.parseBooleanResponse(response).withParseContext(endpoint)
189+ }
190+
191+ fun updateSession (port : Int , sessionId : String , title : String ): ApiResult <Session > {
192+ val endpoint = SessionEndpoints .update(sessionId)
193+ val payload = JsonObject ().apply {
194+ addProperty(" title" , title)
195+ }
196+ val response = transport.patch(port = port, path = endpoint.path, payload = payload.toString())
197+ return transport.mapJsonObjectResponse(response) { parseSession(it) }
198+ .withParseContext(endpoint)
162199 }
163200
164201}
0 commit comments