日志(macOS)

滚动诊断文件日志(Debug 面板) OpenClaw 通过 swift-log(默认使用统一日志)路由 macOS 应用日志,并且在需要持久化捕获时可以将本地轮转文件日志写入磁盘。 详细级别:Debug 面板 → Logs → App logging → Verbosity 启用:Debug 面板 → Logs → App logging → “Write rolling diagnostics log (JSONL)” 位置:~/Library/Logs/OpenClaw/diagnostics.jsonl(自动轮转;旧文件以 .1、.2、… 为后缀) 清除:Debug 面板 → Logs → App logging → “Clear” 注意事项: 此功能默认关闭。仅在主动调试时启用。 该文件包含敏感信息;分享前请先审查内容。 macOS 上统一日志的隐私数据 统一日志会屏蔽大部分负载内容,除非子系统选择启用 privacy -off。根据 Peter 关于 macOS 日志隐私机制(2025)的文章,这通过 /Library/Preferences/Logging/Subsystems/ 中以子系统名称为键的 plist 文件来控制。只有新的日志条目才会应用该标志,因此请在复现问题之前启用它。 为 OpenClaw 启用(bot.molt) 先将 plist 写入临时文件,然后以 root 身份原子性地安装: cat <<'EOF' >/tmp/bot.molt.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>DEFAULT-OPTIONS</key> <dict> <key>Enable-Private-Data</key> <true/> </dict> </dict> </plist> EOF sudo install -m 644 -o root -g wheel /tmp/bot.molt.plist /Library/Preferences/Logging/Subsystems/bot.molt.plist 无需重启;logd 会很快检测到该文件,但只有新的日志行才会包含隐私负载。 使用现有的辅助脚本查看更丰富的输出,例如 ./scripts/clawlog.sh --category WebChat --last 5m。 调试后禁用 移除覆盖配置:sudo rm /Library/Preferences/Logging/Subsystems/bot.molt.plist。 可选择运行 sudo log config --reload 强制 logd 立即丢弃覆盖配置。 请注意此数据可能包含电话号码和消息正文;仅在确实需要额外详细信息时才保留该 plist 文件。

菜单栏图标状态

作者:steipete · 更新时间:2025-12-06 · 范围:macOS 应用(apps/macos) 空闲: 正常图标动画(眨眼、偶尔摆动)。 暂停: 状态项使用 appearsDisabled;无动画。 语音触发(大耳朵): 语音唤醒检测器在听到唤醒词时调用 AppState.triggerVoiceEars(ttl: nil),在捕获语音期间保持 earBoostActive=true。耳朵放大(1.9 倍),显示圆形耳孔以提高可读性,然后在 1 秒静音后通过 stopVoiceEars() 恢复。仅由应用内语音管道触发。 工作中(智能体运行中): AppState.isWorking=true 驱动"尾巴/腿部快速摆动"微动画:工作进行中腿部摆动加快并略有偏移。目前在 WebChat 智能体运行时切换;在接入其他长时间任务时请添加相同的切换逻辑。 接入点 语音唤醒:运行时/测试器在触发时调用 AppState.triggerVoiceEars(ttl: nil),在 1 秒静音后调用 stopVoiceEars() 以匹配捕获窗口。 智能体活动:在工作区间前后设置 AppStateStore.shared.setWorking(true/false)(已在 WebChat 智能体调用中完成)。保持区间简短,并在 defer 块中重置以避免动画卡住。 形状与尺寸 基础图标在 CritterIconRenderer.makeIcon(blink:legWiggle:earWiggle:earScale:earHoles:) 中绘制。 耳朵缩放默认为 1.0;语音增强时设置 earScale=1.9 并切换 earHoles=true,不改变整体框架(18×18 pt 模板图像渲染到 36×36 px Retina 后备存储)。 快速摆动使用最高约 1.0 的腿部摆幅并带有轻微的水平抖动;它与现有的空闲摆动叠加。 行为说明 耳朵/工作状态没有外部 CLI/代理切换;保持仅由应用自身信号控制,以避免意外的状态抖动。 保持 TTL 较短(<10 秒),以便在任务挂起时图标能快速恢复到基准状态。

菜单栏状态逻辑

显示内容 我们在菜单栏图标和菜单的第一行状态行中展示当前智能体的工作状态。 工作活跃时隐藏健康状态;当所有会话空闲时恢复显示。 菜单中的"节点"区块仅列出设备(通过 node.list 配对的节点),不包括客户端/在线状态条目。 当提供商用量快照可用时,“用量"部分会显示在上下文下方。 状态模型 会话:事件携带 runId(每次运行)以及载荷中的 sessionKey。“main” 会话的键为 main;如果不存在,则回退到最近更新的会话。 优先级:main 始终优先。如果 main 处于活跃状态,立即显示其状态。如果 main 空闲,则显示最近活跃的非 main 会话。活动进行中不会来回切换;仅在当前会话进入空闲或 main 变为活跃时才切换。 活动类型: job:高层命令执行(state: started|streaming|done|error)。 tool:phase: start|result,包含 toolName 和 meta/args。 IconState 枚举(Swift) idle workingMain(ActivityKind) workingOther(ActivityKind) overridden(ActivityKind)(调试覆盖) ActivityKind → 图标符号 exec → 💻 read → 📄 write → ✍️ edit → 📝 attach → 📎 默认 → 🛠️ 视觉映射 idle:正常小动物图标。 workingMain:带图标符号的徽章,完整色调,腿部"工作"动画。 workingOther:带图标符号的徽章,柔和色调,无快跑动画。 overridden:无论活动状态如何,使用所选的图标符号/色调。 状态行文本(菜单) 工作活跃时:<会话角色> · <活动标签> 示例:Main · exec: pnpm test、Other · read: apps/macos/Sources/OpenClaw/AppState.swift。 空闲时:回退显示健康摘要。 事件接收 来源:控制渠道 agent 事件(ControlChannel.handleAgentEvent)。 解析字段: stream: "job",包含 data.state 用于启动/停止。 stream: "tool",包含 data.phase、name,可选 meta/args。 标签: exec:args.command 的第一行。 read/write:缩短的路径。 edit:路径加上从 meta/diff 计数推断的变更类型。 回退:工具名称。 调试覆盖 设置 ▸ 调试 ▸ “图标覆盖” 选择器: 系统(自动)(默认) 工作中:main(按工具类型) 工作中:other(按工具类型) 空闲 通过 @AppStorage("iconOverride") 存储;映射到 IconState.overridden。 测试清单 触发 main 会话任务:验证图标立即切换且状态行显示 main 标签。 main 空闲时触发非 main 会话任务:图标/状态显示非 main;保持稳定直到完成。 在 other 活跃时启动 main:图标立即切换到 main。 快速连续工具调用:确保徽章不会闪烁(工具结果的 TTL 宽限期)。 所有会话空闲后健康行重新出现。

语音唤醒与按键通话

模式 唤醒词模式(默认):常驻语音识别器等待触发词(swabbleTriggerWords)。匹配时开始捕获,显示带有部分文本的悬浮窗,并在静默后自动发送。 按键通话(按住右 Option 键):按住右 Option 键立即开始捕获——无需触发词。按住时显示悬浮窗;松开后延迟片刻再最终转发,以便你可以调整文本。 运行时行为(唤醒词) 语音识别器位于 VoiceWakeRuntime 中。 仅当唤醒词和下一个词之间有明显停顿(约 0.55 秒间隔)时才触发。悬浮窗/提示音可以在命令开始前的停顿时就启动。 静默窗口:语音流畅时为 2.0 秒,如果只听到触发词则为 5.0 秒。 硬性停止:120 秒,防止会话失控。 会话间去抖动:350 毫秒。 悬浮窗通过 VoiceWakeOverlayController 驱动,带有已提交/临时状态的颜色区分。 发送后,识别器干净地重启以监听下一个触发词。 生命周期不变量 如果启用了语音唤醒且权限已授予,唤醒词识别器应该处于监听状态(除非正在进行显式的按键通话捕获)。 悬浮窗可见性(包括通过 X 按钮手动关闭)绝不能阻止识别器恢复。 悬浮窗卡住的故障模式(之前的问题) 之前,如果悬浮窗卡在可见状态且你手动关闭它,语音唤醒可能会显得"失效",因为运行时的重启尝试可能被悬浮窗可见性阻止,且没有安排后续重启。 加固措施: 唤醒运行时重启不再被悬浮窗可见性阻止。 悬浮窗关闭完成时通过 VoiceSessionCoordinator 触发 VoiceWakeRuntime.refresh(...),因此手动点击 X 关闭总是会恢复监听。 按键通话细节 热键检测使用全局 .flagsChanged 监视器检测右 Option 键(keyCode 61 + .option)。我们只观察事件(不拦截)。 捕获管道位于 VoicePushToTalk 中:立即启动语音识别,将部分结果流式传输到悬浮窗,并在松开时调用 VoiceWakeForwarder。 按键通话开始时,我们暂停唤醒词运行时以避免音频采集冲突;松开后自动重启。 权限:需要麦克风 + 语音识别权限;查看事件需要辅助功能/输入监控批准。 外接键盘:某些键盘可能无法按预期暴露右 Option 键——如果用户报告未响应,提供备用快捷键。 面向用户的设置 语音唤醒开关:启用唤醒词运行时。 按住 Cmd+Fn 说话:启用按键通话监视器。在 macOS < 26 上禁用。 语言和麦克风选择器、实时电平指示器、触发词表、测试器(仅本地;不转发)。 麦克风选择器在设备断开时保留上次选择,显示断开提示,并临时回退到系统默认设备直到设备恢复。 声音:触发检测和发送时的提示音;默认为 macOS"Glass"系统声音。你可以为每个事件选择任何 NSSound 可加载的文件(例如 MP3/WAV/AIFF)或选择无声音。 转发行为 启用语音唤醒时,转录文本被转发到活动的 Gateway 网关/智能体(与 Mac 应用其他部分使用相同的本地/远程模式)。 回复被投递到上次使用的主提供商(WhatsApp/Telegram/Discord/WebChat)。如果投递失败,错误会被记录,运行记录仍可通过 WebChat/会话日志查看。 转发负载 VoiceWakeForwarder.prefixedTranscript(_:) 在发送前添加机器提示前缀。唤醒词和按键通话路径共享此方法。 快速验证 开启按键通话,按住 Cmd+Fn,说话,松开:悬浮窗应显示部分结果然后发送。 按住时,菜单栏耳朵图标应保持放大状态(使用 triggerVoiceEars(ttl:nil));松开后恢复。

语音浮层生命周期(macOS)

受众:macOS 应用贡献者。目标:在唤醒词与按键说话重叠时保持语音浮层行为可预测。 当前意图 如果浮层已因唤醒词显示,此时用户按下热键,热键会话会接管现有文本而非重置。浮层在热键按住期间保持显示。用户松开时:如果有去除空白后的文本则发送,否则关闭。 单独使用唤醒词时仍在静音后自动发送;按键说话在松开时立即发送。 已实现(2025 年 12 月 9 日) 浮层会话现在为每次捕获(唤醒词或按键说话)携带一个令牌。当令牌不匹配时,部分/最终/发送/关闭/音量更新会被丢弃,避免过时回调。 按键说话会接管任何可见的浮层文本作为前缀(因此在唤醒浮层显示时按下热键会保留文本并追加新语音)。它最多等待 1.5 秒获取最终转录结果,然后回退到当前文本。 提示音/浮层日志以 info 级别输出,分类为 voicewake.overlay、voicewake.ptt 和 voicewake.chime(会话开始、部分、最终、发送、关闭、提示音原因)。 后续步骤 VoiceSessionCoordinator(actor) 同一时间只拥有一个 VoiceSession。 API(基于令牌):beginWakeCapture、beginPushToTalk、updatePartial、endCapture、cancel、applyCooldown。 丢弃携带过时令牌的回调(防止旧识别器重新打开浮层)。 VoiceSession(模型) 字段:token、source(wakeWord|pushToTalk)、已提交/临时文本、提示音标志、计时器(自动发送、空闲)、overlayMode(display|editing|sending)、冷却截止时间。 浮层绑定 VoiceSessionPublisher(ObservableObject)将活跃会话镜像到 SwiftUI。 VoiceWakeOverlayView 仅通过 publisher 渲染;绝不直接修改全局单例。 浮层用户操作(sendNow、dismiss、edit)携带会话令牌回调到 coordinator。 统一发送路径 endCapture 时:如果去除空白后文本为空 → 关闭;否则 performSend(session:)(播放一次发送提示音、转发、关闭)。 按键说话:无延迟;唤醒词:可选自动发送延迟。 按键说话结束后对唤醒运行时施加短暂冷却,防止唤醒词立即重新触发。 日志 Coordinator 在子系统 bot.molt、分类 voicewake.overlay 和 voicewake.chime 下输出 .info 级别日志。 关键事件:session_started、adopted_by_push_to_talk、partial、finalized、send、dismiss、cancel、cooldown。 调试清单 复现浮层粘滞问题时流式查看日志: sudo log stream --predicate 'subsystem == "bot.molt" AND category CONTAINS "voicewake"' --level info --style compact 验证只有一个活跃会话令牌;过时回调应被 coordinator 丢弃。 ...

远程 OpenClaw(macOS ⇄ 远程主机)

此流程让 macOS 应用作为运行在另一台主机(桌面/服务器)上的 OpenClaw Gateway 网关的完整远程控制。这是应用的 Remote over SSH(远程运行)功能。所有功能——健康检查、语音唤醒转发和 Web Chat——都重用来自 Settings → General 的相同远程 SSH 配置。 模式 Local (this Mac):一切都在笔记本电脑上运行。不涉及 SSH。 Remote over SSH(默认):OpenClaw 命令在远程主机上执行。mac 应用使用 -o BatchMode 加上你选择的身份/密钥打开 SSH 连接,并进行本地端口转发。 Remote direct (ws/wss):无 SSH 隧道。mac 应用直接连接到 Gateway 网关 URL(例如,通过 Tailscale Serve 或公共 HTTPS 反向代理)。 远程传输 远程模式支持两种传输方式: SSH 隧道(默认):使用 ssh -N -L ... 将 Gateway 网关端口转发到 localhost。Gateway 网关会将节点的 IP 视为 127.0.0.1,因为隧道是 loopback。 Direct (ws/wss):直接连接到 Gateway 网关 URL。Gateway 网关看到真实的客户端 IP。 远程主机上的先决条件 安装 Node + pnpm 并构建/安装 OpenClaw CLI(pnpm install && pnpm build && pnpm link --global)。 确保 openclaw 在非交互式 shell 的 PATH 中(如需要,请符号链接到 /usr/local/bin 或 /opt/homebrew/bin)。 使用密钥认证打开 SSH。我们推荐使用 Tailscale IP 以实现离开局域网时的稳定可达性。 macOS 应用设置 打开 Settings → General。 在 OpenClaw runs 下,选择 Remote over SSH 并设置: Transport:SSH tunnel 或 Direct (ws/wss)。 SSH target:user@host(可选 :port)。 如果 Gateway 网关在同一局域网上并广播 Bonjour,从发现列表中选择它以自动填充此字段。 Gateway URL(仅 Direct):wss://gateway.example.ts.net(或本地/局域网使用 ws://...)。 Identity file(高级):你的密钥路径。 Project root(高级):用于命令的远程 checkout 路径。 CLI path(高级):可运行的 openclaw 入口点/二进制文件的可选路径(广播时自动填充)。 点击 Test remote。成功表示远程 openclaw status --json 正确运行。失败通常意味着 PATH/CLI 问题;退出码 127 表示远程找不到 CLI。 健康检查和 Web Chat 现在将自动通过此 SSH 隧道运行。 Web Chat SSH 隧道:Web Chat 通过转发的 WebSocket 控制端口(默认 18789)连接到 Gateway 网关。 Direct (ws/wss):Web Chat 直接连接到配置的 Gateway 网关 URL。 不再有单独的 WebChat HTTP 服务器。 权限 远程主机需要与本地相同的 TCC 批准(自动化、辅助功能、屏幕录制、麦克风、语音识别、通知)。在该机器上运行新手引导以一次性授予它们。 节点通过 node.list / node.describe 广播其权限状态,以便智能体知道哪些可用。 安全注意事项 优先在远程主机上使用 loopback 绑定,并通过 SSH 或 Tailscale 连接。 如果你将 Gateway 网关绑定到非 loopback 接口,请要求令牌/密码认证。 参见安全和 Tailscale。 WhatsApp 登录流程(远程) 在远程主机上运行 openclaw channels login --verbose。用手机上的 WhatsApp 扫描二维码。 如果认证过期,在该主机上重新运行登录。健康检查会显示关联问题。 故障排除 exit 127 / not found:openclaw 不在非登录 shell 的 PATH 中。将其添加到 /etc/paths、你的 shell rc,或符号链接到 /usr/local/bin//opt/homebrew/bin。 Health probe failed:检查 SSH 可达性、PATH,以及 Baileys 是否已登录(openclaw status --json)。 Web Chat 卡住:确认 Gateway 网关正在远程主机上运行,转发的端口与 Gateway 网关 WS 端口匹配;UI 需要健康的 WS 连接。 节点 IP 显示 127.0.0.1:使用 SSH 隧道时是预期的。如果你想让 Gateway 网关看到真实的客户端 IP,请将 Transport 切换到 Direct (ws/wss)。 Voice Wake:触发短语在远程模式下自动转发;不需要单独的转发器。 通知声音 通过带有 openclaw 和 node.invoke 的脚本为每个通知选择声音,例如: ...