mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(macos): preserve native command ownership and consent (#118914)
This commit is contained in:
committed by
GitHub
parent
3c858f7a92
commit
7ae6a950f6
@@ -186,9 +186,6 @@ actor MacNodeRuntime {
|
||||
/// One branch per advertised native command keeps command ownership explicit.
|
||||
func handleInvoke(_ req: BridgeInvokeRequest) async -> BridgeInvokeResponse {
|
||||
let command = req.command
|
||||
if let nodeHostWorker, await nodeHostWorker.supports(command) {
|
||||
return await nodeHostWorker.invoke(req)
|
||||
}
|
||||
if self.isCanvasCommand(command), !Self.canvasEnabled() {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
@@ -230,6 +227,9 @@ actor MacNodeRuntime {
|
||||
MacNodeClaudeSessionCatalogContract.readCommand:
|
||||
return try await self.handleClaudeSessionInvoke(req)
|
||||
default:
|
||||
if let nodeHostWorker, await nodeHostWorker.supports(command) {
|
||||
return await nodeHostWorker.invoke(req)
|
||||
}
|
||||
return Self.errorResponse(req, code: .invalidRequest, message: "INVALID_REQUEST: unknown command")
|
||||
}
|
||||
} catch let error as MacNodeCodexThreadCatalog.CatalogError {
|
||||
|
||||
@@ -6,13 +6,17 @@ import Testing
|
||||
private struct WorkerBackpressureTimeout: Error {}
|
||||
|
||||
private actor StubMacNodeHostWorker: MacNodeHostWorking {
|
||||
let manifest = MacNodeHostManifest(
|
||||
version: "test",
|
||||
caps: ["system", "mcp"],
|
||||
commands: ["system.run", "mcp.tools.call.v1"],
|
||||
pathEnv: "/usr/bin:/bin")
|
||||
let manifest: MacNodeHostManifest
|
||||
private var requests: [BridgeInvokeRequest] = []
|
||||
|
||||
init(commands: [String] = ["system.run", "mcp.tools.call.v1"]) {
|
||||
self.manifest = MacNodeHostManifest(
|
||||
version: "test",
|
||||
caps: ["system", "mcp"],
|
||||
commands: commands,
|
||||
pathEnv: "/usr/bin:/bin")
|
||||
}
|
||||
|
||||
func start(command _: [String]) async throws -> MacNodeHostManifest { self.manifest }
|
||||
func supports(_ command: String) async -> Bool { self.manifest.commands.contains(command) }
|
||||
|
||||
@@ -90,18 +94,108 @@ struct MacNodeHostWorkerTests {
|
||||
await worker.stop()
|
||||
}
|
||||
|
||||
@Test func `Mac runtime forwards CLI node commands to the shared worker`() async {
|
||||
let worker = StubMacNodeHostWorker()
|
||||
@Test(arguments: [
|
||||
OpenClawSystemCommand.run.rawValue,
|
||||
"mcp.tools.call.v1",
|
||||
"codex.terminal.resume.v1",
|
||||
])
|
||||
func `Mac runtime forwards worker-owned commands to the shared worker`(command: String) async {
|
||||
let worker = StubMacNodeHostWorker(commands: [command])
|
||||
let runtime = MacNodeRuntime(nodeHostWorker: worker)
|
||||
|
||||
let response = await runtime.handleInvoke(BridgeInvokeRequest(
|
||||
id: "worker-run",
|
||||
command: OpenClawSystemCommand.run.rawValue,
|
||||
command: command,
|
||||
paramsJSON: #"{"command":["/usr/bin/true"]}"#))
|
||||
|
||||
#expect(response.ok)
|
||||
#expect(response.payloadJSON == #"{"owner":"cli"}"#)
|
||||
#expect(await worker.invokedCommands() == [OpenClawSystemCommand.run.rawValue])
|
||||
#expect(await worker.invokedCommands() == [command])
|
||||
}
|
||||
|
||||
@Test(arguments: [
|
||||
MacNodeCodexThreadCatalogContract.listCommand,
|
||||
MacNodeCodexThreadCatalogContract.turnsCommand,
|
||||
MacNodeClaudeSessionCatalogContract.listCommand,
|
||||
MacNodeClaudeSessionCatalogContract.readCommand,
|
||||
])
|
||||
func `native session catalogs own commands shared with the worker`(command: String) async {
|
||||
let worker = StubMacNodeHostWorker(commands: [command])
|
||||
let payload = #"{"owner":"native"}"#
|
||||
let runtime = MacNodeRuntime(
|
||||
nodeHostWorker: worker,
|
||||
codexThreadCatalogEnabled: { true },
|
||||
codexThreadListRequest: { _ in payload },
|
||||
codexThreadTurnsRequest: { _ in payload },
|
||||
claudeSessionCatalogEnabled: { true },
|
||||
claudeSessionListRequest: { _ in payload },
|
||||
claudeSessionReadRequest: { _ in payload })
|
||||
|
||||
let response = await runtime.handleInvoke(BridgeInvokeRequest(
|
||||
id: "native-catalog",
|
||||
command: command,
|
||||
paramsJSON: #"{"limit":1}"#))
|
||||
|
||||
#expect(response.ok)
|
||||
#expect(response.payloadJSON == payload)
|
||||
#expect(await worker.invokedCommands().isEmpty)
|
||||
}
|
||||
|
||||
@Test(arguments: [
|
||||
(
|
||||
MacNodeCodexThreadCatalogContract.listCommand,
|
||||
"UNAVAILABLE: Codex session catalog is disabled"
|
||||
),
|
||||
(
|
||||
MacNodeCodexThreadCatalogContract.turnsCommand,
|
||||
"UNAVAILABLE: Codex session catalog is disabled"
|
||||
),
|
||||
(
|
||||
MacNodeClaudeSessionCatalogContract.listCommand,
|
||||
"UNAVAILABLE: Claude session catalog is disabled"
|
||||
),
|
||||
(
|
||||
MacNodeClaudeSessionCatalogContract.readCommand,
|
||||
"UNAVAILABLE: Claude session catalog is disabled"
|
||||
),
|
||||
(
|
||||
OpenClawComputerCommand.act.rawValue,
|
||||
"COMPUTER_DISABLED: enable Computer Control in Settings"
|
||||
),
|
||||
])
|
||||
func `worker cannot bypass native capability consent`(command: String, expectedMessage: String) async {
|
||||
let worker = StubMacNodeHostWorker(commands: [command])
|
||||
let runtime = MacNodeRuntime(
|
||||
nodeHostWorker: worker,
|
||||
computerControlEnabled: { false },
|
||||
codexThreadCatalogEnabled: { false },
|
||||
claudeSessionCatalogEnabled: { false })
|
||||
|
||||
let response = await runtime.handleInvoke(BridgeInvokeRequest(
|
||||
id: "native-disabled",
|
||||
command: command))
|
||||
|
||||
#expect(!response.ok)
|
||||
#expect(response.error?.code == .unavailable)
|
||||
#expect(response.error?.message == expectedMessage)
|
||||
#expect(await worker.invokedCommands().isEmpty)
|
||||
}
|
||||
|
||||
@Test(arguments: [OpenClawCanvasCommand.present.rawValue, "canvas.plugin.render"])
|
||||
func `worker cannot bypass the canvas namespace consent gate`(command: String) async {
|
||||
await TestIsolation.withUserDefaultsValues([canvasEnabledKey: false]) {
|
||||
let worker = StubMacNodeHostWorker(commands: [command])
|
||||
let runtime = MacNodeRuntime(nodeHostWorker: worker)
|
||||
|
||||
let response = await runtime.handleInvoke(BridgeInvokeRequest(
|
||||
id: "canvas-disabled",
|
||||
command: command))
|
||||
|
||||
#expect(!response.ok)
|
||||
#expect(response.error?.code == .unavailable)
|
||||
#expect(response.error?.message == "CANVAS_DISABLED: enable Canvas in Settings")
|
||||
#expect(await worker.invokedCommands().isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
@Test func `capability union preserves native order and adds worker commands once`() {
|
||||
|
||||
Reference in New Issue
Block a user