# OpenClaw 源码解读（8）：系统底层控制

> OpenClaw 源码解读第 8 篇：Device Nodes 设备节点架构。看 Mac、iPhone、安卓机怎么作为 Node 配对接入 Gateway，node.invoke 从云端到本机的完整调用链，以及截屏、执行命令时如何过 macOS TCC 权限这一关。

- 原文链接: https://laojin.blog/blog/20260318_openclaw_source_code_8
- 作者: 老金
- 发布日期: 2026-03-18
- 标签: OpenClaw, 源码解读, AI Agent, 开源项目

---

在上一篇文章中，我们聊了 OpenClaw 是如何通过跨平台语音唤醒，长出“耳朵”和“嘴巴”的。但这距离一个真正的“全能 AI 管家”还有一段距离。

试想这样一个场景：你正在外面喝咖啡，突然想起来家里电脑上有一个重要进程不知道跑完没有。你对手机里的 Agent 说：“帮我看看家里 Mac 的屏幕，如果跑完了就帮我关机。”

要实现这个场景，Agent 必须拥有突破纯软件沙箱、直接调用底层硬件和系统命令的能力。这就是 OpenClaw 中非常硬核的 Device Nodes（设备节点）架构。今天，老金就带大家深入源码，看看这条从云端大脑直达你电脑底层的“神经通路”是怎么打通的。

## Node 协议：设备如何成为 Agent 的“手脚”？
在 OpenClaw 的世界观里，你的 MacBook、iPhone 甚至是一台吃灰的安卓备用机，都可以运行一个“伴随应用（Companion App）”。当这些应用启动时，它们会作为 Node（节点）加入到 Gateway 的网络中。

这里面最核心的是设备配对与长连接机制。为了保证安全，不是随便哪个设备连上网关就能执行命令的。

```
// src/core/nodes/NodeManager.ts (源码概念简化版)

export class NodeManager {
  /**
   * 处理 Node 节点的 WebSocket 接入请求
   */
  public async handleNodeConnection(socket: WebSocket, req: IncomingMessage) {
    // 1. 提取握手头部的设备 Token 和公钥
    const deviceToken = req.headers['x-node-token'];
    const deviceId = req.headers['x-device-id'];

    // 2. 验证配对状态 (DM Pair 机制)
    const isPaired = await PairingService.verifyDevice(deviceId, deviceToken);
    if (!isPaired) {
      socket.close(4001, 'Unauthorized Device Node');
      return;
    }

    // 3. 注册 Node 会话，维持心跳
    const nodeSession = new NodeSession(deviceId, socket);
    this.activeNodes.set(deviceId, nodeSession);
    
    // 4. 监听来自 Node 的硬件状态上报 (电量、位置等)
    nodeSession.on('status_update', (status) => this.updateNodeStatus(deviceId, status));
  }
}
```

一旦设备配对成功并建立了 WebSocket 连接，这台设备就正式成为了 Agent 可以调用的一个物理终端。

## 宿主调用链：node.invoke 命令执行全流程

当 Agent 决定要控制你的设备时（比如获取摄像头画面），它不会直接写底层代码，而是通过调用内置的工具触发一个极其重要的核心协议：node.invoke。

这是一个类似于 RPC（远程过程调用）的机制。我们来看看一条指令是如何跨越云端落地的。

```
// src/core/tools/nodes/NodeInvokeTool.ts (源码概念简化版)

export class NodeInvokeTool extends Tool {
  name = 'node_invoke';
  description = '向指定的物理设备节点发送底层系统指令。';

  async execute(params: { targetNodeId: string, command: string, args: any }, context: ToolContext) {
    const node = NodeManager.getNode(params.targetNodeId);
    
    // 向目标设备下发指令
    const response = await node.sendAndWait({
      action: 'invoke',
      payload: {
        command: params.command, // 例如：'screen.capture', 'system.run'
        args: params.args
      }
    });

    return response.result;
  }
}
```

Agent 发出指令后，Gateway 只是个传话筒，真正执行脏活累活的，是运行在你设备上的原生伴随应用。

## 打破沙箱：硬件能力与 macOS TCC 权限体系
做过苹果生态开发的朋友肯定知道，macOS 和 iOS 对用户隐私的保护到了极其严苛的程度。你要调用摄像头、麦克风、录制屏幕，甚至获取当前定位，都必须面对大名鼎鼎的 TCC (Transparency, Consent, and Control) 权限控制子系统。

OpenClaw 的 Mac 端节点（使用 Swift 开发）是如何优雅地接住 node.invoke 并打通 TCC 权限的呢？我们深入到 Swift 源码看一看：

```
// apps/apple/OpenClawNode/Commands/NodeCommandDispatcher.swift (源码概念简化版)

class NodeCommandDispatcher {
    func handleInvoke(command: String, args: [String: Any]) async throws -> Any {
        switch command {
        case "screen.capture":
            return try await performScreenCapture()
        case "system.run":
            let script = args["script"] as? String ?? ""
            return try await executeLocalScript(script)
        case "location.get":
            return try await fetchCurrentLocation()
        default:
            throw CommandError.unknownCommand
        }
    }
    
    // 以屏幕录制为例，深究 TCC 权限打通
    private func performScreenCapture() async throws -> String {
        // 1. 检查 macOS 屏幕录制权限 (TCC: NSScreenCapture)
        guard CGPreflightScreenCaptureAccess() else {
            // 如果没权限，主动触发系统设置弹窗，并向 Gateway 返回需要授权的状态
            CGRequestScreenCaptureAccess()
            throw CommandError.permissionDenied(reason: "Please allow Screen Recording in System Settings.")
        }
        
        // 2. 权限校验通过，调用原生 API 截取屏幕
        let screenshot = NativeScreenRecorder.takeSnapshot()
        
        // 3. 转为 Base64 返回给 Gateway
        return screenshot.toBase64()
    }
}
```

高危操作：system.run 的边界

在众多 node.invoke 命令中，最强大也最危险的莫过于 system.run。它允许 Agent 直接在你的宿主机上执行 Bash 脚本（比如 ls -la、git pull 或者是更复杂的环境配置）。

为了防止误操作或被恶意注入，OpenClaw 在端侧做了严格的限制：

 * 沙箱逃逸确认： 
 即使是主人的 Agent 发出的 system.run，如果是极具破坏性的命令（如 rm -rf），端侧应用会弹出一个原生的确认框，必须主人点击“允许”才能放行。
 
 * 环境变量隔离： 
 Agent 执行的脚本运行在一个受限的环境变量子进程中，无法直接窃取用户的系统级 Keychain 或敏感凭证。
 
## 总结
OpenClaw 的 Device Nodes 架构，本质上是在云端的 AI 大脑和本地的物理硬件之间，架设了一道安全的桥梁。

 * WebSocket 与 DM Pair 确保了桥梁的私密性。
 
 * node.invoke 协议 标准化了跨端调用的数据结构。
 
 * 原生端的精细化权限管理（如苹果的 TCC 打通） 则守住了系统安全的最后一道防线。
 
有了这些，你的 AI 助手就不再是飘在云端的一串代码，而是真正落地到了你的电脑和手机里，成为了一个能看、能听、还能帮你敲键盘的实体管家。

到这里，OpenClaw 的核心功能实现我们已经基本摸透了。但作为一个要在生产环境中长期运行的项目，如何部署它？

下一篇，我们将进入本系列的最后一个阶段：安全、部署与运维篇。敬请期待，我们下期见！
