feat(agent): add rpc status command and tests; rpc only path
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
apps/macos/Tests/ClawdisIPCTests/AgentRPCTests.swift
Normal file
17
apps/macos/Tests/ClawdisIPCTests/AgentRPCTests.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user