Co-authored-by: M57 <m57@hermes-control.app>
This commit is contained in:
1 parent
cf893585b1
commit
afa82ceec2
10 files changed
+661
No files matched your search
@@ -0,0 +1,50 @@
|
||||
package com.m57.hermescontrol.data.model
|
||||
|
||||
/**
|
||||
* A single background process owned by a session, as returned by the gateway
|
||||
* `process.list` RPC.
|
||||
*
|
||||
* Field names match the gateway JSON exactly (Gson deserializes by name,
|
||||
* snake_case). See `tui_gateway/server.py::_session_processes` and
|
||||
* `tools/process_registry.list_sessions` for the canonical shape.
|
||||
*
|
||||
* @property sessionId Stable process id used by `process.kill` as `process_id`.
|
||||
* @property command First 200 chars of the spawned command line.
|
||||
* @property cwd Working directory the process was launched in.
|
||||
* @property pid OS process id (0 / null for detached or not-yet-spawned).
|
||||
* @property startedAt ISO-8601 local timestamp string from the backend.
|
||||
* @property uptimeSeconds Seconds since spawn (backend-computed).
|
||||
* @property status `"running"` or `"exited"`.
|
||||
* @property outputPreview Last 200 chars of buffered output.
|
||||
* @property outputTail Last 4000 chars of buffered output (richer preview).
|
||||
* @property exitCode Present only when [status] == `"exited"`.
|
||||
* @property sessionScoped True when surfaced only because it shares the gateway
|
||||
* session (not the current task) — a possibly-forgotten long-lived process.
|
||||
*/
|
||||
data class ProcessInfo(
|
||||
val sessionId: String,
|
||||
val command: String? = null,
|
||||
val cwd: String? = null,
|
||||
val pid: Int? = null,
|
||||
val startedAt: String? = null,
|
||||
val uptimeSeconds: Int? = null,
|
||||
val status: String? = null,
|
||||
val outputPreview: String? = null,
|
||||
val outputTail: String? = null,
|
||||
val exitCode: Int? = null,
|
||||
val sessionScoped: Boolean = false,
|
||||
) {
|
||||
val isRunning: Boolean get() = status != "exited"
|
||||
|
||||
/** A short, human-readable title derived from the command line. */
|
||||
val title: String
|
||||
get() =
|
||||
(command ?: "")
|
||||
.lineSequence()
|
||||
.firstOrNull()
|
||||
?.trim()
|
||||
.orEmpty()
|
||||
.ifEmpty { "background process" }
|
||||
|
||||
companion object
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.m57.hermescontrol.data.session
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/**
|
||||
* App-wide mirror of the active chat session id.
|
||||
*
|
||||
* The desktop `process.list` / `process.kill` RPCs are session-scoped — the
|
||||
* gateway returns `4001 "session not found"` without a valid `session_id`, and
|
||||
* mobile has no global "current session" holder (ChatViewModel keeps it
|
||||
* privately). This singleton holds the last-known active session id so
|
||||
* session-scoped drawer screens (e.g. the Processes screen, issue #532) can
|
||||
* issue those RPCs. ChatViewModel writes here on every switch/resume; it is a
|
||||
* best-effort mirror and may be null when no chat session has been opened yet.
|
||||
*/
|
||||
object ActiveSessionHolder {
|
||||
private val _activeSessionId = MutableStateFlow<String?>(null)
|
||||
|
||||
/** The currently active chat session id, or null if none is known yet. */
|
||||
val activeSessionId: StateFlow<String?> = _activeSessionId.asStateFlow()
|
||||
|
||||
/** Update the active session id. Pass null to clear (e.g. on logout). */
|
||||
fun set(sessionId: String?) {
|
||||
_activeSessionId.value = sessionId
|
||||
}
|
||||
}
|
||||
@@ -32,4 +32,12 @@ object WsMethods {
|
||||
|
||||
/** Stage a non-image file (data URL) for agent access. */
|
||||
const val FILE_ATTACH = "file.attach"
|
||||
|
||||
// ── Background process manager (issue #532) ──────────────────────────
|
||||
|
||||
/** List background processes owned by the active session. */
|
||||
const val PROCESS_LIST = "process.list"
|
||||
|
||||
/** Kill a single background process (scoped to the active session). */
|
||||
const val PROCESS_KILL = "process.kill"
|
||||
}
|
||||
Reference in new issue
Block a user