feat(agent): add rpc status command and tests; rpc only path

This commit is contained in:
Peter Steinberger
2025-12-07 05:20:50 +01:00
parent fb1de5c1c6
commit 32720bd372
5 changed files with 54 additions and 0 deletions

View File

@@ -53,6 +53,26 @@ actor AgentRPC {
}
}
func status() async -> (ok: Bool, error: String?) {
do {
try await ensureRunning()
let payload: [String: Any] = ["type": "status"]
let data = try JSONSerialization.data(withJSONObject: payload)
guard let stdinHandle else { throw RpcError(message: "stdin missing") }
stdinHandle.write(data)
stdinHandle.write(Data([0x0A]))
let line = try await nextLine()
let parsed = try JSONSerialization.jsonObject(with: Data(line.utf8)) as? [String: Any]
if let ok = parsed?["ok"] as? Bool, ok { return (true, nil) }
return (false, parsed?["error"] as? String ?? "rpc status failed")
} catch {
logger.error("rpc status failed: \(error.localizedDescription, privacy: .public)")
await stop()
return (false, error.localizedDescription)
}
}
// MARK: - Process lifecycle
private func ensureRunning() async throws {

View File

@@ -51,6 +51,10 @@ final class ClawdisXPCService: NSObject, ClawdisXPCProtocol {
case .status:
return Response(ok: true, message: "ready")
case .rpcStatus:
let result = await AgentRPC.shared.status()
return Response(ok: result.ok, message: result.error)
case let .screenshot(displayID, windowID, _):
let authorized = await PermissionManager
.ensure([.screenRecording], interactive: false)[.screenRecording] ?? false

View File

@@ -133,6 +133,9 @@ struct ClawdisCLI {
case "status":
return .status
case "rpc-status":
return .rpcStatus
case "agent":
var message: String?
var thinking: String?
@@ -174,6 +177,7 @@ struct ClawdisCLI {
clawdis-mac screenshot [--display-id <u32>] [--window-id <u32>]
clawdis-mac run [--cwd <path>] [--env KEY=VAL] [--timeout <sec>] [--needs-screen-recording] <command ...>
clawdis-mac status
clawdis-mac rpc-status
clawdis-mac agent --message <text> [--thinking <low|default|high>] [--session <key>]
clawdis-mac --help

View File

@@ -26,6 +26,7 @@ public enum Request: Sendable {
needsScreenRecording: Bool)
case status
case agent(message: String, thinking: String?, session: String?)
case rpcStatus
}
// MARK: - Responses
@@ -53,6 +54,7 @@ extension Request: Codable {
case displayID, windowID, format
case command, cwd, env, timeoutSec, needsScreenRecording
case message, thinking, session
case rpcStatus
}
private enum Kind: String, Codable {
@@ -62,6 +64,7 @@ extension Request: Codable {
case runShell
case status
case agent
case rpcStatus
}
public func encode(to encoder: Encoder) throws {
@@ -100,6 +103,9 @@ extension Request: Codable {
try container.encode(message, forKey: .message)
try container.encodeIfPresent(thinking, forKey: .thinking)
try container.encodeIfPresent(session, forKey: .session)
case .rpcStatus:
try container.encode(Kind.rpcStatus, forKey: .type)
}
}
@@ -140,6 +146,9 @@ extension Request: Codable {
let thinking = try container.decodeIfPresent(String.self, forKey: .thinking)
let session = try container.decodeIfPresent(String.self, forKey: .session)
self = .agent(message: message, thinking: thinking, session: session)
case .rpcStatus:
self = .rpcStatus
}
}
}

View File

@@ -0,0 +1,17 @@
import Clawdis
import Testing
@testable import ClawdisIPC
@Suite(.serialized) struct AgentRPCTests {
@Test func statusFailsWhenProcessMissing() async {
let result = await AgentRPC.shared.status()
// We don't assert ok because the worker may not be available in CI.
// Instead, ensure the call returns without throwing and provides a message.
#expect(result.ok == true || result.error != nil)
}
@Test func rejectEmptyMessage() async {
let result = await AgentRPC.shared.send(text: "", thinking: nil, session: "main")
#expect(result.ok == false)
}
}