diff --git a/.agents/skills/parallels-discord-roundtrip/SKILL.md b/.agents/skills/parallels-discord-roundtrip/SKILL.md new file mode 100644 index 00000000000..8fda0da1a23 --- /dev/null +++ b/.agents/skills/parallels-discord-roundtrip/SKILL.md @@ -0,0 +1,59 @@ +--- +name: parallels-discord-roundtrip +description: Run the macOS Parallels smoke harness with Discord end-to-end roundtrip verification, including guest send, host verification, host reply, and guest readback. +--- + +# Parallels Discord Roundtrip + +Use when macOS Parallels smoke must prove Discord two-way delivery end to end. + +## Goal + +Cover: + +- install on fresh macOS snapshot +- onboard + gateway health +- guest `message send` to Discord +- host sees that message on Discord +- host posts a new Discord message +- guest `message read` sees that new message + +## Inputs + +- host env var with Discord bot token +- Discord guild ID +- Discord channel ID +- `OPENAI_API_KEY` + +## Preferred run + +```bash +export OPENCLAW_PARALLELS_DISCORD_TOKEN="$( + ssh peters-mac-studio-1 'jq -r ".channels.discord.token" ~/.openclaw/openclaw.json' | tr -d '\n' +)" + +pnpm test:parallels:macos \ + --discord-token-env OPENCLAW_PARALLELS_DISCORD_TOKEN \ + --discord-guild-id 1456350064065904867 \ + --discord-channel-id 1456744319972282449 \ + --json +``` + +## Notes + +- Snapshot target: closest to `macOS 26.3.1 fresh`. +- Harness configures Discord inside the guest; no checked-in token/config. +- Use the `openclaw` wrapper for guest `message send/read`; `node openclaw.mjs message ...` does not expose the lazy message subcommands the same way. +- Write `channels.discord.guilds` in one JSON object (`--strict-json`), not dotted `config set channels.discord.guilds....` paths; numeric snowflakes get treated like array indexes. +- Avoid `prlctl enter` / expect for long Discord setup scripts; it line-wraps/corrupts long commands. Use `prlctl exec --current-user /bin/sh -lc ...` for the Discord config phase. +- Harness cleanup deletes the temporary Discord smoke messages at exit. +- Per-phase logs: `/tmp/openclaw-parallels-smoke.*` +- Machine summary: pass `--json` +- If roundtrip flakes, inspect `fresh.discord-roundtrip.log` and `discord-last-readback.json` in the run dir first. + +## Pass criteria + +- fresh lane or upgrade lane requested passes +- summary reports `discord=pass` for that lane +- guest outbound nonce appears in channel history +- host inbound nonce appears in `openclaw message read` output diff --git a/AGENTS.md b/AGENTS.md index 1197f6fb48f..df72efbe720 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -212,6 +212,11 @@ - `prlctl exec` is fine for deterministic repo commands, but it can misrepresent interactive shell behavior (`PATH`, `HOME`, `curl | bash`, shebang resolution). For installer parity or shell-sensitive repros, prefer the guest Terminal or `prlctl enter`. - Fresh Tahoe snapshot current reality: `brew` exists, `node` may not be on `PATH` in noninteractive guest exec. Use absolute `/opt/homebrew/bin/node` for repo/CLI runs when needed. - Preferred automation entrypoint: `pnpm test:parallels:macos`. It restores the snapshot most closely matching `macOS 26.3.1 fresh`, serves the current `main` tarball from the host, then runs fresh-install and latest-release-to-main smoke lanes. + - Discord roundtrip smoke is opt-in. Pass `--discord-token-env --discord-guild-id --discord-channel-id `; the harness will configure Discord in-guest, post a guest message, verify host-side visibility via the Discord REST API, post a fresh host-side message back into the channel, then verify `openclaw message read` sees it in-guest. + - Keep the Discord token in a host env var only. For Peter’s Mac Studio bot, fetch it into a temp env var from `~/.openclaw/openclaw.json` over SSH instead of hardcoding it in repo files/shell history. + - For Discord smoke on this snapshot: use `openclaw message send/read` via the installed wrapper, not `node openclaw.mjs message ...`; lazy `message` subcommands do not resolve the same way through the direct module entrypoint. + - For Discord guild allowlists: set `channels.discord.guilds` as one JSON object. Do not use dotted `config set channels.discord.guilds....` paths; numeric snowflakes get treated as array indexes. + - Avoid `prlctl enter` / expect for the Discord config phase; long lines get mangled. Use `prlctl exec --current-user /bin/sh -lc ...` with short commands or temp files. - Gateway verification in smoke runs should use `openclaw gateway status --deep --require-rpc`, not plain `--deep`, so probe failures go non-zero. - Latest-release pre-upgrade diagnostics still need compatibility fallback: stable `2026.3.12` does not know `--require-rpc`, so precheck status dumps should fall back to plain `gateway status --deep` until the guest is upgraded. - Harness output: pass `--json` for machine-readable summary; per-phase logs land under `/tmp/openclaw-parallels-smoke.*`. diff --git a/CHANGELOG.md b/CHANGELOG.md index ce45052af01..df03ad8fc5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,81 +6,92 @@ Docs: https://docs.openclaw.ai ### Changes -- Android/mobile: add a system-aware dark theme across onboarding and post-onboarding screens so the app follows the device theme through setup, chat, and voice flows. (#46249) Thanks @sibbl. - Commands/btw: add `/btw` side questions for quick tool-less answers about the current session without changing future session context, with dismissible in-session TUI answers and explicit BTW replies on external channels. (#45444) Thanks @ngutman. -- Gateway/health monitor: add configurable stale-event thresholds and restart limits, plus per-channel and per-account `healthMonitor.enabled` overrides, while keeping the existing global disable path on `gateway.channelHealthCheckMinutes=0`. (#42107) Thanks @rstar327. -- Feishu/cards: add identity-aware structured card headers and note footers for Feishu replies and direct sends, while keeping that presentation wired through the shared outbound identity path. (#29938) Thanks @nszhsl. -- Feishu/streaming: add `onReasoningStream` and `onReasoningEnd` support to streaming cards, so `/reasoning stream` renders thinking tokens as markdown blockquotes in the same card — matching the Telegram channel's reasoning lane behavior. (#46029) -- Web tools/Firecrawl: add Firecrawl as an `onboard`/configure search provider via a bundled plugin, expose explicit `firecrawl_search` and `firecrawl_scrape` tools, and align core `web_fetch` fallback behavior with Firecrawl base-URL/env fallback plus guarded endpoint fetches. -- Refactor/channels: remove the legacy channel shim directories and point channel-specific imports directly at the extension-owned implementations. (#45967) thanks @scoootscooob. -- Android/nodes: add `callLog.search` plus shared Call Log permission wiring so Android nodes can search recent call history through the gateway. (#44073) Thanks @lxk7280. -- Docs/Zalo: clarify the Marketplace-bot support matrix and config guidance so the Zalo channel docs match current Bot Creator behavior more closely. (#47552) Thanks @No898. -- Install/update: allow package-manager installs from GitHub `main` via `openclaw update --tag main`, installer `--version main`, or direct npm/pnpm git specs. -- Plugins/providers: move OpenRouter, GitHub Copilot, and OpenAI Codex provider/runtime logic into bundled plugins, including dynamic model fallback, runtime auth exchange, stream wrappers, capability hints, and cache-TTL policy. -- Plugins/MiniMax: merge the bundled MiniMax API and MiniMax OAuth plugin surfaces into a single default-on `minimax` plugin, while keeping legacy `minimax-portal-auth` config ids aliased for compatibility. -- Plugins/bundles: add compatible Codex, Claude, and Cursor bundle discovery/install support, map bundle skills into OpenClaw skills, and apply Claude bundle `settings.json` defaults to embedded Pi with shell overrides sanitized. -- Plugins/agent integrations: broaden the plugin surface for app-server integrations with channel-aware commands, interactive callbacks, inbound claims, and Discord/Telegram conversation binding support. (#45318) Thanks @huntharo and @vincentkoc. -- Telegram/actions: add `topic-edit` for forum-topic renames and icon updates while sharing the same Telegram topic-edit transport used by the plugin runtime. (#47798) Thanks @obviyus. -- secrets: harden read-only SecretRef command paths and diagnostics. (#47794) Thanks @joshavant. - Sandbox/runtime: add pluggable sandbox backends, ship an OpenShell backend with `mirror` and `remote` workspace modes, and make sandbox list/recreate/prune backend-aware instead of Docker-only. - Sandbox/SSH: add a core SSH sandbox backend with secret-backed key, certificate, and known_hosts inputs, move shared remote exec/filesystem tooling into core, and keep OpenShell focused on sandbox lifecycle plus optional `mirror` mode. +- Web tools/Firecrawl: add Firecrawl as an `onboard`/configure search provider via a bundled plugin, expose explicit `firecrawl_search` and `firecrawl_scrape` tools, and align core `web_fetch` fallback behavior with Firecrawl base-URL/env fallback plus guarded endpoint fetches. +- Plugins/bundles: add compatible Codex, Claude, and Cursor bundle discovery/install support, map bundle skills into OpenClaw skills, and apply Claude bundle `settings.json` defaults to embedded Pi with shell overrides sanitized. +- Plugins/providers: move OpenRouter, GitHub Copilot, and OpenAI Codex provider/runtime logic into bundled plugins, including dynamic model fallback, runtime auth exchange, stream wrappers, capability hints, and cache-TTL policy. +- Plugins/agent integrations: broaden the plugin surface for app-server integrations with channel-aware commands, interactive callbacks, inbound claims, and Discord/Telegram conversation binding support. (#45318) Thanks @huntharo and @vincentkoc. +- Install/update: allow package-manager installs from GitHub `main` via `openclaw update --tag main`, installer `--version main`, or direct npm/pnpm git specs. +- Gateway/health monitor: add configurable stale-event thresholds and restart limits, plus per-channel and per-account `healthMonitor.enabled` overrides, while keeping the existing global disable path on `gateway.channelHealthCheckMinutes=0`. (#42107) Thanks @rstar327. +- Android/mobile: add a system-aware dark theme across onboarding and post-onboarding screens so the app follows the device theme through setup, chat, and voice flows. (#46249) Thanks @sibbl. +- Feishu/ACP: add current-conversation ACP and subagent session binding for supported DMs and topic conversations, including completion delivery back to the originating Feishu conversation. (#46819) +- Plugins/marketplaces: add Claude marketplace registry resolution, `plugin@marketplace` installs, marketplace listing, and update support, plus Docker E2E coverage for local and official marketplace flows. Thanks @vincentkoc. +- Feishu/cards: add structured interactive approval and quick-action launcher cards, preserve callback user and conversation context through routing, and keep legacy card-action fallback behavior so common actions can run without typing raw commands. (#47873) +- Feishu/streaming: add `onReasoningStream` and `onReasoningEnd` support to streaming cards, so `/reasoning stream` renders thinking tokens as markdown blockquotes in the same card — matching the Telegram channel's reasoning lane behavior. (#46029) +- Feishu/cards: add identity-aware structured card headers and note footers for Feishu replies and direct sends, while keeping that presentation wired through the shared outbound identity path. (#29938) Thanks @nszhsl. +- Android/nodes: add `callLog.search` plus shared Call Log permission wiring so Android nodes can search recent call history through the gateway. (#44073) Thanks @lxk7280. +- Plugins/MiniMax: merge the bundled MiniMax API and MiniMax OAuth plugin surfaces into a single default-on `minimax` plugin, while keeping legacy `minimax-portal-auth` config ids aliased for compatibility. +- Telegram/actions: add `topic-edit` for forum-topic renames and icon updates while sharing the same Telegram topic-edit transport used by the plugin runtime. (#47798) Thanks @obviyus. +- Refactor/channels: remove the legacy channel shim directories and point channel-specific imports directly at the extension-owned implementations. (#45967) thanks @scoootscooob. +- Docs/Zalo: clarify the Marketplace-bot support matrix and config guidance so the Zalo channel docs match current Bot Creator behavior more closely. (#47552) Thanks @No898. +- secrets: harden read-only SecretRef command paths and diagnostics. (#47794) Thanks @joshavant. + +### Breaking + +- Browser/Chrome MCP: remove the legacy Chrome extension relay path, bundled extension assets, `driver: "extension"`, and `browser.relayBindHost`. Run `openclaw doctor --fix` to migrate host-local browser config to `existing-session` / `user`; Docker, headless, sandbox, and remote browser flows still use raw CDP. Thanks @vincentkoc. ### Fixes -- Group mention gating: reject invalid and unsafe nested-repetition `mentionPatterns`, reuse the shared safe config-regex compiler across mention stripping and detection, and cache strip-time regex compilation so noisy groups avoid repeated recompiles. -- Control UI/chat sessions: show human-readable labels in the grouped session dropdown again, keep unique scoped fallbacks when metadata is missing, and disambiguate duplicate labels only when needed. (#45130) thanks @luzhidong. -- Control UI: scope persisted session selection per gateway, prevent stale session bleed across tokenized gateway opens, and cap stored gateway session history. (#47453) Thanks @sallyom. -- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. (#45890) Thanks @vincentkoc. -- Feishu/topic threads: fetch full thread context, including prior bot replies, when starting a topic-thread session so follow-up turns in Feishu topics keep the right conversation state. (#45254) Thanks @Coobiw. -- Configure/startup: move outbound send-deps resolution into a lightweight helper so `openclaw configure` no longer stalls after the banner while eagerly loading channel plugins. (#46301) thanks @scoootscooob. -- Control UI/dashboard: preserve structured gateway shutdown reasons across restart disconnects so config-triggered restarts no longer fall back to `disconnected (1006): no reason`. (#46532) Thanks @vincentkoc. -- Android/chat: theme the thinking dropdown and TLS trust dialogs explicitly so popup surfaces match the active app theme instead of falling back to mismatched Material defaults. -- Z.AI/onboarding: detect a working default model even for explicit `zai-coding-*` endpoint choices, so Coding Plan setup can keep the selected endpoint while defaulting to `glm-5` when available or `glm-4.7` as fallback. (#45969) -- Models/OpenRouter runtime capabilities: fetch uncatalogued OpenRouter model metadata on first use so newly added vision models keep image input instead of silently degrading to text-only, with top-level capability field fallbacks for `/api/v1/models`. (#45824) Thanks @DJjjjhao. -- Z.AI/onboarding: add `glm-5-turbo` to the default Z.AI provider catalog so onboarding-generated configs expose the new model alongside the existing GLM defaults. (#46670) Thanks @tomsun28. -- Zalo Personal/group gating: stop reapplying `dmPolicy.allowFrom` as a sender gate for already-allowlisted groups when `groupAllowFrom` is unset, so any member of an allowed group can trigger replies while DMs stay restricted. (#40146) -- Channels/plugins: keep shared interactive payloads merge-ready by fixing Slack custom callback routing and repeat-click dedupe, allowing interactive-only sends, and preserving ordered Discord shared text blocks. (#47715) Thanks @vincentkoc. +- Google auth/Node 25: patch `gaxios` to use native fetch without injecting `globalThis.window`, while translating proxy and mTLS transport settings so Google Vertex and Google Chat auth keep working on Node 25. (#47914) Thanks @pdd-cli. +- Gateway/startup: load bundled channel plugins from compiled `dist/extensions` entries in built installs, so gateway boot no longer recompiles bundled extension TypeScript on every startup and WhatsApp-class cold starts drop back to seconds instead of tens of seconds or worse. +- Plugins/context engines: enforce owner-aware context-engine registration on both loader and public SDK paths so plugins cannot spoof privileged ownership, claim the core `legacy` engine id, or overwrite an existing engine id through direct SDK imports. (#47595) Thanks @vincentkoc. - Browser/remote CDP: honor strict browser SSRF policy during remote CDP reachability and `/json/version` discovery checks, redact sensitive `cdpUrl` tokens from status output, and warn when remote CDP targets private/internal hosts. -- Plugins/install precedence: keep bundled plugins ahead of auto-discovered globals by default, but let an explicitly installed plugin record win its own duplicate-id tie so installed channel plugins load from `~/.openclaw/extensions` after `openclaw plugins install`. -- ACP/acpx: resolve the bundled plugin root from the actual plugin directory so plugin-local installs stay under `dist/extensions/acpx` instead of escaping to `dist/extensions` and failing runtime setup. -- Gateway/auth: ignore spoofed loopback hops in trusted forwarding chains and block device approvals that request scopes above the caller session. Thanks @vincentkoc. -- Gateway/config views: strip embedded credentials from URL-based endpoint fields before returning read-only account and config snapshots. Thanks @vincentkoc. -- Tools/apply-patch: revalidate workspace-only delete and directory targets immediately before mutating host paths. Thanks @vincentkoc. -- Webhooks/runtime: move auth earlier and tighten pre-auth body limits and timeouts across bundled webhook handlers, including slow-body handling for Mattermost slash commands. Thanks @vincentkoc. - Gateway/plugins: pin runtime webhook routes to the gateway startup registry so channel webhooks keep working across plugin-registry churn, and make plugin auth + dispatch resolve routes from the same live HTTP-route registry. Fixes #46924 and #47041. -- Subagents/follow-ups: require the same controller ownership checks for `/subagents send` as other control actions, so leaf sessions cannot message nested child runs they do not control. Thanks @vincentkoc. -- Inbound policy hardening: tighten callback and webhook sender checks across Mattermost and Google Chat, match Nextcloud Talk rooms by stable room token, and treat explicit empty Twitch allowlists as deny-all. (#46787) Thanks @zpbrent, @ijxpwastaken and @vincentkoc. -- macOS/canvas actions: keep unattended local agent actions on trusted in-app canvas surfaces only, and stop exposing the deep-link fallback key to arbitrary page scripts. (#46790) Thanks @vincentkoc. -- Agents/compaction: extend the enclosing run deadline once while compaction is actively in flight, and abort the underlying SDK compaction on timeout/cancel so large-session compactions stop freezing mid-run. (#46889) Thanks @asyncjason. -- Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition `strict` fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava. -- WhatsApp/reconnect: restore the append recency filter in the extension inbox monitor and handle protobuf `Long` timestamps correctly, so fresh post-reconnect append messages are processed while stale history sync stays suppressed. (#42588) thanks @MonkeyLeeT. -- WhatsApp/login: wait for pending creds writes before reopening after Baileys `515` pairing restarts in both QR login and `channels login` flows, and keep the restart coverage pinned to the real wrapped error shape plus per-account creds queues. (#27910) Thanks @asyncjason. -- Agents/openai-compatible tool calls: deduplicate repeated tool call ids across live assistant messages and replayed history so OpenAI-compatible backends no longer reject duplicate `tool_call_id` values with HTTP 400. (#40996) Thanks @xaeon2026. -- Security/device pairing: harden `device.token.rotate` deny handling by keeping public failures generic while logging internal deny reasons and preserving approved-baseline enforcement. (`GHSA-7jrw-x62h-64p8`) -- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. (#45890) Thanks @vincentkoc. -- Zalo/plugin runtime: export `resolveClientIp` from `openclaw/plugin-sdk/zalo` so installed builds no longer crash on startup when the webhook monitor loads from the packaged extension instead of the monorepo source tree. (#46549) Thanks @No898. -- CI/channel test routing: move the built-in channel suites into `test:channels` and keep them out of `test:extensions`, so extension CI no longer fails after the channel migration while targeted test routing still sends Slack, Signal, and iMessage suites to the right lane. (#46066) Thanks @scoootscooob. -- Browser/profiles: drop the auto-created `chrome-relay` browser profile; users who need the Chrome extension relay must now create their own profile via `openclaw browser create-profile`. (#45777) Thanks @odysseus0. -- Docs/Mintlify: fix MDX marker syntax on Perplexity, Model Providers, Moonshot, and exec approvals pages so local docs preview no longer breaks rendering or leaves stale pages unpublished. (#46695) Thanks @velvet-shark. -- Email/webhook wrapping: sanitize sender and subject metadata before external-content wrapping so metadata fields cannot break the wrapper structure. Thanks @vincentkoc. -- Node/startup: remove leftover debug `console.log("node host PATH: ...")` that printed the resolved PATH on every `openclaw node run` invocation. (#46411) -- Nodes/pending actions: re-check queued foreground actions against the current node command policy before returning them to the node. (#46815) Thanks @zpbrent and @vincentkoc. -- ACP/approvals: use canonical tool identity for prompting decisions and fail closed when conflicting tool identity hints are present. (#46817) Thanks @zpbrent and @vincentkoc. -- Telegram/message send: forward `--force-document` through the `sendPayload` path as well as `sendMedia`, so Telegram payload sends with `channelData` keep uploading images as documents instead of silently falling back to compressed photo sends. (#47119) Thanks @thepagent. -- Telegram/message chunking: preserve spaces, paragraph separators, and word boundaries when HTML overflow rechunking splits formatted replies. (#47274) -- Plugins/scoped ids: preserve scoped plugin ids during install and config keying, and keep bundled plugins ahead of discovered duplicate ids by default so `@scope/name` plugins no longer collide with unscoped installs. Thanks @vincentkoc. -- CLI: avoid loading provider discovery during startup model normalization. (#46522) Thanks @ItsAditya-xyz and @vincentkoc. -- Tlon: honor explicit empty allowlists and defer cite expansion. (#46788) Thanks @zpbrent and @vincentkoc. -- ACP: require admin scope for mutating internal actions. (#46789) Thanks @tdjackey and @vincentkoc. -- Gateway/config validation: stop treating the implicit default memory slot as a required explicit plugin config, so startup no longer fails with `plugins.slots.memory: plugin not found: memory-core` when `memory-core` was only inferred. (#47494) Thanks @ngutman. +- Gateway/auth: ignore spoofed loopback hops in trusted forwarding chains and block device approvals that request scopes above the caller session. Thanks @vincentkoc. +- Gateway/restart: defer externally signaled unmanaged restarts through the in-process idle drain, and preserve the restored subagent run as remap fallback during orphan recovery so resumed sessions do not duplicate work. (#47719) Thanks @joeykrug. +- Control UI/session routing: preserve established external delivery routes when webchat views or sends in externally originated sessions, so subagent completions still return to the original channel instead of the dashboard. (#47797) Thanks @brokemac79. +- Configure/startup: move outbound send-deps resolution into a lightweight helper so `openclaw configure` no longer stalls after the banner while eagerly loading channel plugins. (#46301) thanks @scoootscooob. - CLI/startup: lazy-load channel add and root help startup paths to trim avoidable RSS and help latency on constrained hosts. (#46784) Thanks @vincentkoc. - CLI/onboarding: import static provider definitions directly for onboarding model/config helpers so those paths no longer pull provider discovery just for built-in defaults. (#47467) Thanks @vincentkoc. - CLI/auth choice: lazy-load plugin/provider fallback resolution so mapped auth choices stay on the static path and only unknown choices pay the heavy provider load. (#47495) Thanks @vincentkoc. -- CLI/completion: reduce recursive completion-script string churn and fix nested PowerShell command-path matching so generated nested completions resolve on PowerShell too. (#45537) Thanks @yiShanXin and @vincentkoc. -- Gateway/startup: load bundled channel plugins from compiled `dist/extensions` entries in built installs, so gateway boot no longer recompiles bundled extension TypeScript on every startup and WhatsApp-class cold starts drop back to seconds instead of tens of seconds or worse. +- CLI: avoid loading provider discovery during startup model normalization. (#46522) Thanks @ItsAditya-xyz and @vincentkoc. +- Security/device pairing: harden `device.token.rotate` deny handling by keeping public failures generic while logging internal deny reasons and preserving approved-baseline enforcement. (`GHSA-7jrw-x62h-64p8`) +- Inbound policy hardening: tighten callback and webhook sender checks across Mattermost and Google Chat, match Nextcloud Talk rooms by stable room token, and treat explicit empty Twitch allowlists as deny-all. (#46787) Thanks @zpbrent, @ijxpwastaken and @vincentkoc. +- Webhooks/runtime: move auth earlier and tighten pre-auth body limits and timeouts across bundled webhook handlers, including slow-body handling for Mattermost slash commands. Thanks @vincentkoc. +- Email/webhook wrapping: sanitize sender and subject metadata before external-content wrapping so metadata fields cannot break the wrapper structure. Thanks @vincentkoc. +- Tools/apply-patch: revalidate workspace-only delete and directory targets immediately before mutating host paths. Thanks @vincentkoc. +- Gateway/config views: strip embedded credentials from URL-based endpoint fields before returning read-only account and config snapshots. Thanks @vincentkoc. +- ACP/approvals: use canonical tool identity for prompting decisions and fail closed when conflicting tool identity hints are present. (#46817) Thanks @zpbrent and @vincentkoc. +- ACP: require admin scope for mutating internal actions. (#46789) Thanks @tdjackey and @vincentkoc. +- Subagents/follow-ups: require the same controller ownership checks for `/subagents send` as other control actions, so leaf sessions cannot message nested child runs they do not control. Thanks @vincentkoc. +- macOS/canvas actions: keep unattended local agent actions on trusted in-app canvas surfaces only, and stop exposing the deep-link fallback key to arbitrary page scripts. (#46790) Thanks @vincentkoc. +- Agents/compaction: extend the enclosing run deadline once while compaction is actively in flight, and abort the underlying SDK compaction on timeout/cancel so large-session compactions stop freezing mid-run. (#46889) Thanks @asyncjason. +- Agents/openai-compatible tool calls: deduplicate repeated tool call ids across live assistant messages and replayed history so OpenAI-compatible backends no longer reject duplicate `tool_call_id` values with HTTP 400. (#40996) Thanks @xaeon2026. +- Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition `strict` fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava. +- Models/OpenRouter runtime capabilities: fetch uncatalogued OpenRouter model metadata on first use so newly added vision models keep image input instead of silently degrading to text-only, with top-level capability field fallbacks for `/api/v1/models`. (#45824) Thanks @DJjjjhao. +- Channels/plugins: keep shared interactive payloads merge-ready by fixing Slack custom callback routing and repeat-click dedupe, allowing interactive-only sends, and preserving ordered Discord shared text blocks. (#47715) Thanks @vincentkoc. +- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. (#45890) Thanks @vincentkoc. +- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. (#45890) Thanks @vincentkoc. +- Feishu/actions: expand the runtime action surface with message read/edit, explicit thread replies, pinning, and operator-facing chat/member inspection so Feishu can operate more of the workspace directly. +- Feishu/topic threads: fetch full thread context, including prior bot replies, when starting a topic-thread session so follow-up turns in Feishu topics keep the right conversation state. (#45254) Thanks @Coobiw. +- Feishu/media: keep native image, file, audio, and video/media handling aligned across outbound sends, inbound downloads, thread replies, directory/action aliases, and capability docs so unsupported areas are explicit instead of implied. +- WhatsApp/reconnect: restore the append recency filter in the extension inbox monitor and handle protobuf `Long` timestamps correctly, so fresh post-reconnect append messages are processed while stale history sync stays suppressed. (#42588) thanks @MonkeyLeeT. +- WhatsApp/login: wait for pending creds writes before reopening after Baileys `515` pairing restarts in both QR login and `channels login` flows, and keep the restart coverage pinned to the real wrapped error shape plus per-account creds queues. (#27910) Thanks @asyncjason. +- Telegram/message send: forward `--force-document` through the `sendPayload` path as well as `sendMedia`, so Telegram payload sends with `channelData` keep uploading images as documents instead of silently falling back to compressed photo sends. (#47119) Thanks @thepagent. +- Telegram/message chunking: preserve spaces, paragraph separators, and word boundaries when HTML overflow rechunking splits formatted replies. (#47274) +- Z.AI/onboarding: detect a working default model even for explicit `zai-coding-*` endpoint choices, so Coding Plan setup can keep the selected endpoint while defaulting to `glm-5` when available or `glm-4.7` as fallback. (#45969) +- Z.AI/onboarding: add `glm-5-turbo` to the default Z.AI provider catalog so onboarding-generated configs expose the new model alongside the existing GLM defaults. (#46670) Thanks @tomsun28. +- Zalo Personal/group gating: stop reapplying `dmPolicy.allowFrom` as a sender gate for already-allowlisted groups when `groupAllowFrom` is unset, so any member of an allowed group can trigger replies while DMs stay restricted. (#40146) +- Zalo/plugin runtime: export `resolveClientIp` from `openclaw/plugin-sdk/zalo` so installed builds no longer crash on startup when the webhook monitor loads from the packaged extension instead of the monorepo source tree. (#46549) Thanks @No898. +- Plugins/install precedence: keep bundled plugins ahead of auto-discovered globals by default, but let an explicitly installed plugin record win its own duplicate-id tie so installed channel plugins load from `~/.openclaw/extensions` after `openclaw plugins install`. +- Plugins/scoped ids: preserve scoped plugin ids during install and config keying, and keep bundled plugins ahead of discovered duplicate ids by default so `@scope/name` plugins no longer collide with unscoped installs. Thanks @vincentkoc. - Gateway/watch mode: restart on bundled-plugin package and manifest metadata changes, rebuild `dist` for extension source and `tsdown.config.ts` changes, and still ignore extension docs. (#47571) thanks @gumadeiras. - Gateway/watch mode: recreate bundled plugin runtime metadata after clean or stale `dist` states, so `pnpm gateway:watch` no longer fails on missing `dist/extensions/*/openclaw.plugin.json` manifests after a rebuild. Thanks @gumadeiras. -- Plugins/context engines: enforce owner-aware context-engine registration on both loader and public SDK paths so plugins cannot spoof privileged ownership, claim the core `legacy` engine id, or overwrite an existing engine id through direct SDK imports. (#47595) Thanks @vincentkoc. +- Control UI/chat sessions: show human-readable labels in the grouped session dropdown again, keep unique scoped fallbacks when metadata is missing, and disambiguate duplicate labels only when needed. (#45130) thanks @luzhidong. +- Control UI: scope persisted session selection per gateway, prevent stale session bleed across tokenized gateway opens, and cap stored gateway session history. (#47453) Thanks @sallyom. +- Control UI/dashboard: preserve structured gateway shutdown reasons across restart disconnects so config-triggered restarts no longer fall back to `disconnected (1006): no reason`. (#46532) Thanks @vincentkoc. +- Android/chat: theme the thinking dropdown and TLS trust dialogs explicitly so popup surfaces match the active app theme instead of falling back to mismatched Material defaults. +- Group mention gating: reject invalid and unsafe nested-repetition `mentionPatterns`, reuse the shared safe config-regex compiler across mention stripping and detection, and cache strip-time regex compilation so noisy groups avoid repeated recompiles. +- Browser/profiles: drop the auto-created `chrome-relay` browser profile; users who need the Chrome extension relay must now create their own profile via `openclaw browser create-profile`. (#45777) Thanks @odysseus0. +- CI/channel test routing: move the built-in channel suites into `test:channels` and keep them out of `test:extensions`, so extension CI no longer fails after the channel migration while targeted test routing still sends Slack, Signal, and iMessage suites to the right lane. (#46066) Thanks @scoootscooob. +- Docs/Mintlify: fix MDX marker syntax on Perplexity, Model Providers, Moonshot, and exec approvals pages so local docs preview no longer breaks rendering or leaves stale pages unpublished. (#46695) Thanks @velvet-shark. +- Gateway/config validation: stop treating the implicit default memory slot as a required explicit plugin config, so startup no longer fails with `plugins.slots.memory: plugin not found: memory-core` when `memory-core` was only inferred. (#47494) Thanks @ngutman. +- Tlon: honor explicit empty allowlists and defer cite expansion. (#46788) Thanks @zpbrent and @vincentkoc. +- Nodes/pending actions: re-check queued foreground actions against the current node command policy before returning them to the node. (#46815) Thanks @zpbrent and @vincentkoc. +- Node/startup: remove leftover debug `console.log("node host PATH: ...")` that printed the resolved PATH on every `openclaw node run` invocation. (#46411) +- CLI/completion: reduce recursive completion-script string churn and fix nested PowerShell command-path matching so generated nested completions resolve on PowerShell too. (#45537) Thanks @yiShanXin and @vincentkoc. ## 2026.3.13 diff --git a/apps/macos/Sources/OpenClaw/CanvasA2UIActionMessageHandler.swift b/apps/macos/Sources/OpenClaw/CanvasA2UIActionMessageHandler.swift index c81d4b59705..0599f4ab3a6 100644 --- a/apps/macos/Sources/OpenClaw/CanvasA2UIActionMessageHandler.swift +++ b/apps/macos/Sources/OpenClaw/CanvasA2UIActionMessageHandler.swift @@ -8,6 +8,24 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler { static let messageName = "openclawCanvasA2UIAction" static let allMessageNames = [messageName] + // Compatibility helper for debug/test shims. Runtime dispatch remains + // limited to in-app canvas schemes in `didReceive`. + static func isLocalNetworkCanvasURL(_ url: URL) -> Bool { + guard let scheme = url.scheme?.lowercased(), scheme == "http" || scheme == "https" else { + return false + } + guard let host = url.host?.lowercased(), !host.isEmpty else { + return false + } + if host == "localhost" { + return true + } + guard let ip = Self.parseIPv4(host) else { + return false + } + return Self.isLocalNetworkIPv4(ip) + } + private let sessionKey: String init(sessionKey: String) { @@ -104,5 +122,24 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler { } } } + + private static func parseIPv4(_ host: String) -> (UInt8, UInt8, UInt8, UInt8)? { + let parts = host.split(separator: ".", omittingEmptySubsequences: false) + guard parts.count == 4 else { return nil } + let bytes = parts.compactMap { UInt8($0) } + guard bytes.count == 4 else { return nil } + return (bytes[0], bytes[1], bytes[2], bytes[3]) + } + + private static func isLocalNetworkIPv4(_ ip: (UInt8, UInt8, UInt8, UInt8)) -> Bool { + let (a, b, _, _) = ip + if a == 10 { return true } + if a == 172, (16...31).contains(Int(b)) { return true } + if a == 192, b == 168 { return true } + if a == 127 { return true } + if a == 169, b == 254 { return true } + if a == 100, (64...127).contains(Int(b)) { return true } + return false + } // Formatting helpers live in OpenClawKit (`OpenClawCanvasA2UIAction`). } diff --git a/apps/macos/Sources/OpenClaw/CronModels.swift b/apps/macos/Sources/OpenClaw/CronModels.swift index 40079453974..78016ff9f88 100644 --- a/apps/macos/Sources/OpenClaw/CronModels.swift +++ b/apps/macos/Sources/OpenClaw/CronModels.swift @@ -254,6 +254,71 @@ struct CronJob: Identifiable, Codable, Equatable { case state } + init( + id: String, + agentId: String?, + name: String, + description: String?, + enabled: Bool, + deleteAfterRun: Bool?, + createdAtMs: Int, + updatedAtMs: Int, + schedule: CronSchedule, + sessionTarget: CronSessionTarget, + wakeMode: CronWakeMode, + payload: CronPayload, + delivery: CronDelivery?, + state: CronJobState) + { + self.init( + id: id, + agentId: agentId, + name: name, + description: description, + enabled: enabled, + deleteAfterRun: deleteAfterRun, + createdAtMs: createdAtMs, + updatedAtMs: updatedAtMs, + schedule: schedule, + sessionTarget: .predefined(sessionTarget), + wakeMode: wakeMode, + payload: payload, + delivery: delivery, + state: state) + } + + init( + id: String, + agentId: String?, + name: String, + description: String?, + enabled: Bool, + deleteAfterRun: Bool?, + createdAtMs: Int, + updatedAtMs: Int, + schedule: CronSchedule, + sessionTarget: CronCustomSessionTarget, + wakeMode: CronWakeMode, + payload: CronPayload, + delivery: CronDelivery?, + state: CronJobState) + { + self.id = id + self.agentId = agentId + self.name = name + self.description = description + self.enabled = enabled + self.deleteAfterRun = deleteAfterRun + self.createdAtMs = createdAtMs + self.updatedAtMs = updatedAtMs + self.schedule = schedule + self.sessionTargetRaw = sessionTarget.rawValue + self.wakeMode = wakeMode + self.payload = payload + self.delivery = delivery + self.state = state + } + /// Parsed session target (predefined or custom session ID) var parsedSessionTarget: CronCustomSessionTarget { CronCustomSessionTarget.from(self.sessionTargetRaw) diff --git a/assets/chrome-extension/README.md b/assets/chrome-extension/README.md deleted file mode 100644 index 4ee072c1f2b..00000000000 --- a/assets/chrome-extension/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# OpenClaw Chrome Extension (Browser Relay) - -Purpose: attach OpenClaw to an existing Chrome tab so the Gateway can automate it (via the local CDP relay server). - -## Dev / load unpacked - -1. Build/run OpenClaw Gateway with browser control enabled. -2. Ensure the relay server is reachable at `http://127.0.0.1:18792/` (default). -3. Install the extension to a stable path: - - ```bash - openclaw browser extension install - openclaw browser extension path - ``` - -4. Chrome → `chrome://extensions` → enable “Developer mode”. -5. “Load unpacked” → select the path printed above. -6. Pin the extension. Click the icon on a tab to attach/detach. - -## Options - -- `Relay port`: defaults to `18792`. -- `Gateway token`: required. Set this to `gateway.auth.token` (or `OPENCLAW_GATEWAY_TOKEN`). diff --git a/assets/chrome-extension/background-utils.js b/assets/chrome-extension/background-utils.js deleted file mode 100644 index 82d43359c0a..00000000000 --- a/assets/chrome-extension/background-utils.js +++ /dev/null @@ -1,64 +0,0 @@ -export function reconnectDelayMs( - attempt, - opts = { baseMs: 1000, maxMs: 30000, jitterMs: 1000, random: Math.random }, -) { - const baseMs = Number.isFinite(opts.baseMs) ? opts.baseMs : 1000; - const maxMs = Number.isFinite(opts.maxMs) ? opts.maxMs : 30000; - const jitterMs = Number.isFinite(opts.jitterMs) ? opts.jitterMs : 1000; - const random = typeof opts.random === "function" ? opts.random : Math.random; - const safeAttempt = Math.max(0, Number.isFinite(attempt) ? attempt : 0); - const backoff = Math.min(baseMs * 2 ** safeAttempt, maxMs); - return backoff + Math.max(0, jitterMs) * random(); -} - -export async function deriveRelayToken(gatewayToken, port) { - const enc = new TextEncoder(); - const key = await crypto.subtle.importKey( - "raw", - enc.encode(gatewayToken), - { name: "HMAC", hash: "SHA-256" }, - false, - ["sign"], - ); - const sig = await crypto.subtle.sign( - "HMAC", - key, - enc.encode(`openclaw-extension-relay-v1:${port}`), - ); - return [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, "0")).join(""); -} - -export async function buildRelayWsUrl(port, gatewayToken) { - const token = String(gatewayToken || "").trim(); - if (!token) { - throw new Error( - "Missing gatewayToken in extension settings (chrome.storage.local.gatewayToken)", - ); - } - const relayToken = await deriveRelayToken(token, port); - return `ws://127.0.0.1:${port}/extension?token=${encodeURIComponent(relayToken)}`; -} - -export function isRetryableReconnectError(err) { - const message = err instanceof Error ? err.message : String(err || ""); - if (message.includes("Missing gatewayToken")) { - return false; - } - return true; -} - -export function isMissingTabError(err) { - const message = (err instanceof Error ? err.message : String(err || "")).toLowerCase(); - return ( - message.includes("no tab with id") || - message.includes("no tab with given id") || - message.includes("tab not found") - ); -} - -export function isLastRemainingTab(allTabs, tabIdToClose) { - if (!Array.isArray(allTabs)) { - return true; - } - return allTabs.filter((tab) => tab && tab.id !== tabIdToClose).length === 0; -} diff --git a/assets/chrome-extension/background.js b/assets/chrome-extension/background.js deleted file mode 100644 index 9031a156489..00000000000 --- a/assets/chrome-extension/background.js +++ /dev/null @@ -1,1025 +0,0 @@ -import { - buildRelayWsUrl, - isLastRemainingTab, - isMissingTabError, - isRetryableReconnectError, - reconnectDelayMs, -} from './background-utils.js' - -const DEFAULT_PORT = 18792 - -const BADGE = { - on: { text: 'ON', color: '#FF5A36' }, - off: { text: '', color: '#000000' }, - connecting: { text: '…', color: '#F59E0B' }, - error: { text: '!', color: '#B91C1C' }, -} - -/** @type {WebSocket|null} */ -let relayWs = null -/** @type {Promise|null} */ -let relayConnectPromise = null -let relayGatewayToken = '' -/** @type {string|null} */ -let relayConnectRequestId = null - -let nextSession = 1 - -/** @type {Map} */ -const tabs = new Map() -/** @type {Map} */ -const tabBySession = new Map() -/** @type {Map} */ -const childSessionToTab = new Map() - -/** @type {Mapvoid, reject:(e:Error)=>void}>} */ -const pending = new Map() - -// Per-tab operation locks prevent double-attach races. -/** @type {Set} */ -const tabOperationLocks = new Set() - -// Tabs currently in a detach/re-attach cycle after navigation. -/** @type {Set} */ -const reattachPending = new Set() - -// Reconnect state for exponential backoff. -let reconnectAttempt = 0 -let reconnectTimer = null - -const TAB_VALIDATION_ATTEMPTS = 2 -const TAB_VALIDATION_RETRY_DELAY_MS = 1000 - -function nowStack() { - try { - return new Error().stack || '' - } catch { - return '' - } -} - -function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)) -} - -async function validateAttachedTab(tabId) { - try { - await chrome.tabs.get(tabId) - } catch { - return false - } - - for (let attempt = 0; attempt < TAB_VALIDATION_ATTEMPTS; attempt++) { - try { - await chrome.debugger.sendCommand({ tabId }, 'Runtime.evaluate', { - expression: '1', - returnByValue: true, - }) - return true - } catch (err) { - if (isMissingTabError(err)) { - return false - } - if (attempt < TAB_VALIDATION_ATTEMPTS - 1) { - await sleep(TAB_VALIDATION_RETRY_DELAY_MS) - } - } - } - - return false -} - -async function getRelayPort() { - const stored = await chrome.storage.local.get(['relayPort']) - const raw = stored.relayPort - const n = Number.parseInt(String(raw || ''), 10) - if (!Number.isFinite(n) || n <= 0 || n > 65535) return DEFAULT_PORT - return n -} - -async function getGatewayToken() { - const stored = await chrome.storage.local.get(['gatewayToken']) - const token = String(stored.gatewayToken || '').trim() - return token || '' -} - -function setBadge(tabId, kind) { - const cfg = BADGE[kind] - void chrome.action.setBadgeText({ tabId, text: cfg.text }) - void chrome.action.setBadgeBackgroundColor({ tabId, color: cfg.color }) - void chrome.action.setBadgeTextColor({ tabId, color: '#FFFFFF' }).catch(() => {}) -} - -// Persist attached tab state to survive MV3 service worker restarts. -async function persistState() { - try { - const tabEntries = [] - for (const [tabId, tab] of tabs.entries()) { - if (tab.state === 'connected' && tab.sessionId && tab.targetId) { - tabEntries.push({ tabId, sessionId: tab.sessionId, targetId: tab.targetId, attachOrder: tab.attachOrder }) - } - } - await chrome.storage.session.set({ - persistedTabs: tabEntries, - nextSession, - }) - } catch { - // chrome.storage.session may not be available in all contexts. - } -} - -// Rehydrate tab state on service worker startup. Fast path — just restores -// maps and badges. Relay reconnect happens separately in background. -async function rehydrateState() { - try { - const stored = await chrome.storage.session.get(['persistedTabs', 'nextSession']) - if (stored.nextSession) { - nextSession = Math.max(nextSession, stored.nextSession) - } - const entries = stored.persistedTabs || [] - // Phase 1: optimistically restore state and badges. - for (const entry of entries) { - tabs.set(entry.tabId, { - state: 'connected', - sessionId: entry.sessionId, - targetId: entry.targetId, - attachOrder: entry.attachOrder, - }) - tabBySession.set(entry.sessionId, entry.tabId) - setBadge(entry.tabId, 'on') - } - // Retry once so transient busy/navigation states do not permanently drop - // a still-attached tab after a service worker restart. - for (const entry of entries) { - const valid = await validateAttachedTab(entry.tabId) - if (!valid) { - tabs.delete(entry.tabId) - tabBySession.delete(entry.sessionId) - setBadge(entry.tabId, 'off') - } - } - } catch { - // Ignore rehydration errors. - } -} - -async function ensureRelayConnection() { - if (relayWs && relayWs.readyState === WebSocket.OPEN) return - if (relayConnectPromise) return await relayConnectPromise - - relayConnectPromise = (async () => { - const port = await getRelayPort() - const gatewayToken = await getGatewayToken() - const httpBase = `http://127.0.0.1:${port}` - const wsUrl = await buildRelayWsUrl(port, gatewayToken) - - // Fast preflight: is the relay server up? - try { - await fetch(`${httpBase}/`, { method: 'HEAD', signal: AbortSignal.timeout(2000) }) - } catch (err) { - throw new Error(`Relay server not reachable at ${httpBase} (${String(err)})`) - } - - const ws = new WebSocket(wsUrl) - relayWs = ws - relayGatewayToken = gatewayToken - // Bind message handler before open so an immediate first frame (for example - // gateway connect.challenge) cannot be missed. - ws.onmessage = (event) => { - if (ws !== relayWs) return - void whenReady(() => onRelayMessage(String(event.data || ''))) - } - - await new Promise((resolve, reject) => { - const t = setTimeout(() => reject(new Error('WebSocket connect timeout')), 5000) - ws.onopen = () => { - clearTimeout(t) - resolve() - } - ws.onerror = () => { - clearTimeout(t) - reject(new Error('WebSocket connect failed')) - } - ws.onclose = (ev) => { - clearTimeout(t) - reject(new Error(`WebSocket closed (${ev.code} ${ev.reason || 'no reason'})`)) - } - }) - - // Bind permanent handlers. Guard against stale socket: if this WS was - // replaced before its close fires, the handler is a no-op. - ws.onclose = () => { - if (ws !== relayWs) return - onRelayClosed('closed') - } - ws.onerror = () => { - if (ws !== relayWs) return - onRelayClosed('error') - } - })() - - try { - await relayConnectPromise - reconnectAttempt = 0 - } finally { - relayConnectPromise = null - } -} - -// Relay closed — update badges, reject pending requests, auto-reconnect. -// Debugger sessions are kept alive so they survive transient WS drops. -function onRelayClosed(reason) { - relayWs = null - relayGatewayToken = '' - relayConnectRequestId = null - - for (const [id, p] of pending.entries()) { - pending.delete(id) - p.reject(new Error(`Relay disconnected (${reason})`)) - } - - reattachPending.clear() - - for (const [tabId, tab] of tabs.entries()) { - if (tab.state === 'connected') { - setBadge(tabId, 'connecting') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: relay reconnecting…', - }) - } - } - - scheduleReconnect() -} - -function scheduleReconnect() { - if (reconnectTimer) { - clearTimeout(reconnectTimer) - reconnectTimer = null - } - - const delay = reconnectDelayMs(reconnectAttempt) - reconnectAttempt++ - - console.log(`Scheduling reconnect attempt ${reconnectAttempt} in ${Math.round(delay)}ms`) - - reconnectTimer = setTimeout(async () => { - reconnectTimer = null - try { - await ensureRelayConnection() - reconnectAttempt = 0 - console.log('Reconnected successfully') - await reannounceAttachedTabs() - } catch (err) { - const message = err instanceof Error ? err.message : String(err) - console.warn(`Reconnect attempt ${reconnectAttempt} failed: ${message}`) - if (!isRetryableReconnectError(err)) { - return - } - scheduleReconnect() - } - }, delay) -} - -function cancelReconnect() { - if (reconnectTimer) { - clearTimeout(reconnectTimer) - reconnectTimer = null - } - reconnectAttempt = 0 -} - -// Re-announce all attached tabs to the relay after reconnect. -async function reannounceAttachedTabs() { - for (const [tabId, tab] of tabs.entries()) { - if (tab.state !== 'connected' || !tab.sessionId || !tab.targetId) continue - - // Retry once here as well; reconnect races can briefly make an otherwise - // healthy tab look unavailable. - const valid = await validateAttachedTab(tabId) - if (!valid) { - tabs.delete(tabId) - if (tab.sessionId) tabBySession.delete(tab.sessionId) - setBadge(tabId, 'off') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay (click to attach/detach)', - }) - continue - } - - // Send fresh attach event to relay. - // Split into two try-catch blocks so debugger failures and relay send - // failures are handled independently. Previously, a relay send failure - // would fall into the outer catch and set the badge to 'on' even though - // the relay had no record of the tab — causing every subsequent browser - // tool call to fail with "no tab connected" until the next reconnect cycle. - let targetInfo - try { - const info = /** @type {any} */ ( - await chrome.debugger.sendCommand({ tabId }, 'Target.getTargetInfo') - ) - targetInfo = info?.targetInfo - } catch { - // Target.getTargetInfo failed. Preserve at least targetId from - // cached tab state so relay receives a stable identifier. - targetInfo = tab.targetId ? { targetId: tab.targetId } : undefined - } - - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.attachedToTarget', - params: { - sessionId: tab.sessionId, - targetInfo: { ...targetInfo, attached: true }, - waitingForDebugger: false, - }, - }, - }) - - setBadge(tabId, 'on') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: attached (click to detach)', - }) - } catch { - // Relay send failed (e.g. WS closed in the gap between ensureRelayConnection - // resolving and this loop executing). The tab is still valid — leave badge - // as 'connecting' so the reconnect/keepalive cycle will retry rather than - // showing a false-positive 'on' that hides the broken state from the user. - setBadge(tabId, 'connecting') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: relay reconnecting…', - }) - } - } - - await persistState() -} - -function sendToRelay(payload) { - const ws = relayWs - if (!ws || ws.readyState !== WebSocket.OPEN) { - throw new Error('Relay not connected') - } - ws.send(JSON.stringify(payload)) -} - -function ensureGatewayHandshakeStarted(payload) { - if (relayConnectRequestId) return - const nonce = typeof payload?.nonce === 'string' ? payload.nonce.trim() : '' - relayConnectRequestId = `ext-connect-${Date.now()}-${Math.random().toString(16).slice(2, 8)}` - sendToRelay({ - type: 'req', - id: relayConnectRequestId, - method: 'connect', - params: { - minProtocol: 3, - maxProtocol: 3, - client: { - id: 'chrome-relay-extension', - version: '1.0.0', - platform: 'chrome-extension', - mode: 'webchat', - }, - role: 'operator', - scopes: ['operator.read', 'operator.write'], - caps: [], - commands: [], - nonce: nonce || undefined, - auth: relayGatewayToken ? { token: relayGatewayToken } : undefined, - }, - }) -} - -async function maybeOpenHelpOnce() { - try { - const stored = await chrome.storage.local.get(['helpOnErrorShown']) - if (stored.helpOnErrorShown === true) return - await chrome.storage.local.set({ helpOnErrorShown: true }) - await chrome.runtime.openOptionsPage() - } catch { - // ignore - } -} - -function requestFromRelay(command) { - const id = command.id - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - pending.delete(id) - reject(new Error('Relay request timeout (30s)')) - }, 30000) - pending.set(id, { - resolve: (v) => { clearTimeout(timer); resolve(v) }, - reject: (e) => { clearTimeout(timer); reject(e) }, - }) - try { - sendToRelay(command) - } catch (err) { - clearTimeout(timer) - pending.delete(id) - reject(err instanceof Error ? err : new Error(String(err))) - } - }) -} - -async function onRelayMessage(text) { - /** @type {any} */ - let msg - try { - msg = JSON.parse(text) - } catch { - return - } - - if (msg && msg.type === 'event' && msg.event === 'connect.challenge') { - try { - ensureGatewayHandshakeStarted(msg.payload) - } catch (err) { - console.warn('gateway connect handshake start failed', err instanceof Error ? err.message : String(err)) - relayConnectRequestId = null - const ws = relayWs - if (ws && ws.readyState === WebSocket.OPEN) { - ws.close(1008, 'gateway connect failed') - } - } - return - } - - if (msg && msg.type === 'res' && relayConnectRequestId && msg.id === relayConnectRequestId) { - relayConnectRequestId = null - if (!msg.ok) { - const detail = msg?.error?.message || msg?.error || 'gateway connect failed' - console.warn('gateway connect handshake rejected', String(detail)) - const ws = relayWs - if (ws && ws.readyState === WebSocket.OPEN) { - ws.close(1008, 'gateway connect failed') - } - } - return - } - - if (msg && msg.method === 'ping') { - try { - sendToRelay({ method: 'pong' }) - } catch { - // ignore - } - return - } - - if (msg && typeof msg.id === 'number' && (msg.result !== undefined || msg.error !== undefined)) { - const p = pending.get(msg.id) - if (!p) return - pending.delete(msg.id) - if (msg.error) p.reject(new Error(String(msg.error))) - else p.resolve(msg.result) - return - } - - if (msg && typeof msg.id === 'number' && msg.method === 'forwardCDPCommand') { - try { - const result = await handleForwardCdpCommand(msg) - sendToRelay({ id: msg.id, result }) - } catch (err) { - sendToRelay({ id: msg.id, error: err instanceof Error ? err.message : String(err) }) - } - } -} - -function getTabBySessionId(sessionId) { - const direct = tabBySession.get(sessionId) - if (direct) return { tabId: direct, kind: 'main' } - const child = childSessionToTab.get(sessionId) - if (child) return { tabId: child, kind: 'child' } - return null -} - -function getTabByTargetId(targetId) { - for (const [tabId, tab] of tabs.entries()) { - if (tab.targetId === targetId) return tabId - } - return null -} - -async function attachTab(tabId, opts = {}) { - const debuggee = { tabId } - await chrome.debugger.attach(debuggee, '1.3') - await chrome.debugger.sendCommand(debuggee, 'Page.enable').catch(() => {}) - - const info = /** @type {any} */ (await chrome.debugger.sendCommand(debuggee, 'Target.getTargetInfo')) - const targetInfo = info?.targetInfo - const targetId = String(targetInfo?.targetId || '').trim() - if (!targetId) { - throw new Error('Target.getTargetInfo returned no targetId') - } - - const sid = nextSession++ - const sessionId = `cb-tab-${sid}` - const attachOrder = sid - - tabs.set(tabId, { state: 'connected', sessionId, targetId, attachOrder }) - tabBySession.set(sessionId, tabId) - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: attached (click to detach)', - }) - - if (!opts.skipAttachedEvent) { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.attachedToTarget', - params: { - sessionId, - targetInfo: { ...targetInfo, attached: true }, - waitingForDebugger: false, - }, - }, - }) - } - - setBadge(tabId, 'on') - await persistState() - - return { sessionId, targetId } -} - -async function detachTab(tabId, reason) { - const tab = tabs.get(tabId) - - // Send detach events for child sessions first. - for (const [childSessionId, parentTabId] of childSessionToTab.entries()) { - if (parentTabId === tabId) { - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.detachedFromTarget', - params: { sessionId: childSessionId, reason: 'parent_detached' }, - }, - }) - } catch { - // Relay may be down. - } - childSessionToTab.delete(childSessionId) - } - } - - // Send detach event for main session. - if (tab?.sessionId && tab?.targetId) { - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.detachedFromTarget', - params: { sessionId: tab.sessionId, targetId: tab.targetId, reason }, - }, - }) - } catch { - // Relay may be down. - } - } - - if (tab?.sessionId) tabBySession.delete(tab.sessionId) - tabs.delete(tabId) - - try { - await chrome.debugger.detach({ tabId }) - } catch { - // May already be detached. - } - - setBadge(tabId, 'off') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay (click to attach/detach)', - }) - - await persistState() -} - -async function connectOrToggleForActiveTab() { - const [active] = await chrome.tabs.query({ active: true, currentWindow: true }) - const tabId = active?.id - if (!tabId) return - - // Prevent concurrent operations on the same tab. - if (tabOperationLocks.has(tabId)) return - tabOperationLocks.add(tabId) - - try { - if (reattachPending.has(tabId)) { - reattachPending.delete(tabId) - setBadge(tabId, 'off') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay (click to attach/detach)', - }) - return - } - - const existing = tabs.get(tabId) - if (existing?.state === 'connected') { - await detachTab(tabId, 'toggle') - return - } - - // User is manually connecting — cancel any pending reconnect. - cancelReconnect() - - tabs.set(tabId, { state: 'connecting' }) - setBadge(tabId, 'connecting') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: connecting to local relay…', - }) - - try { - await ensureRelayConnection() - await attachTab(tabId) - } catch (err) { - tabs.delete(tabId) - setBadge(tabId, 'error') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: relay not running (open options for setup)', - }) - void maybeOpenHelpOnce() - const message = err instanceof Error ? err.message : String(err) - console.warn('attach failed', message, nowStack()) - } - } finally { - tabOperationLocks.delete(tabId) - } -} - -async function handleForwardCdpCommand(msg) { - const method = String(msg?.params?.method || '').trim() - const params = msg?.params?.params || undefined - const sessionId = typeof msg?.params?.sessionId === 'string' ? msg.params.sessionId : undefined - - const bySession = sessionId ? getTabBySessionId(sessionId) : null - const targetId = typeof params?.targetId === 'string' ? params.targetId : undefined - const tabId = - bySession?.tabId || - (targetId ? getTabByTargetId(targetId) : null) || - (() => { - for (const [id, tab] of tabs.entries()) { - if (tab.state === 'connected') return id - } - return null - })() - - if (!tabId) throw new Error(`No attached tab for method ${method}`) - - /** @type {chrome.debugger.DebuggerSession} */ - const debuggee = { tabId } - - if (method === 'Runtime.enable') { - try { - await chrome.debugger.sendCommand(debuggee, 'Runtime.disable') - await new Promise((r) => setTimeout(r, 50)) - } catch { - // ignore - } - return await chrome.debugger.sendCommand(debuggee, 'Runtime.enable', params) - } - - if (method === 'Target.createTarget') { - const url = typeof params?.url === 'string' ? params.url : 'about:blank' - const tab = await chrome.tabs.create({ url, active: false }) - if (!tab.id) throw new Error('Failed to create tab') - await new Promise((r) => setTimeout(r, 100)) - const attached = await attachTab(tab.id) - return { targetId: attached.targetId } - } - - if (method === 'Target.closeTarget') { - const target = typeof params?.targetId === 'string' ? params.targetId : '' - const toClose = target ? getTabByTargetId(target) : tabId - if (!toClose) return { success: false } - try { - const allTabs = await chrome.tabs.query({}) - if (isLastRemainingTab(allTabs, toClose)) { - console.warn('Refusing to close the last tab: this would kill the browser process') - return { success: false, error: 'Cannot close the last tab' } - } - await chrome.tabs.remove(toClose) - } catch { - return { success: false } - } - return { success: true } - } - - if (method === 'Target.activateTarget') { - const target = typeof params?.targetId === 'string' ? params.targetId : '' - const toActivate = target ? getTabByTargetId(target) : tabId - if (!toActivate) return {} - const tab = await chrome.tabs.get(toActivate).catch(() => null) - if (!tab) return {} - if (tab.windowId) { - await chrome.windows.update(tab.windowId, { focused: true }).catch(() => {}) - } - await chrome.tabs.update(toActivate, { active: true }).catch(() => {}) - return {} - } - - const tabState = tabs.get(tabId) - const mainSessionId = tabState?.sessionId - const debuggerSession = - sessionId && mainSessionId && sessionId !== mainSessionId - ? { ...debuggee, sessionId } - : debuggee - - return await chrome.debugger.sendCommand(debuggerSession, method, params) -} - -function onDebuggerEvent(source, method, params) { - const tabId = source.tabId - if (!tabId) return - const tab = tabs.get(tabId) - if (!tab?.sessionId) return - - if (method === 'Target.attachedToTarget' && params?.sessionId) { - childSessionToTab.set(String(params.sessionId), tabId) - } - - if (method === 'Target.detachedFromTarget' && params?.sessionId) { - childSessionToTab.delete(String(params.sessionId)) - } - - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - sessionId: source.sessionId || tab.sessionId, - method, - params, - }, - }) - } catch { - // Relay may be down. - } -} - -async function onDebuggerDetach(source, reason) { - const tabId = source.tabId - if (!tabId) return - if (!tabs.has(tabId)) return - - // User explicitly cancelled or DevTools replaced the connection — respect their intent - if (reason === 'canceled_by_user' || reason === 'replaced_with_devtools') { - void detachTab(tabId, reason) - return - } - - // Check if tab still exists — distinguishes navigation from tab close - let tabInfo - try { - tabInfo = await chrome.tabs.get(tabId) - } catch { - // Tab is gone (closed) — normal cleanup - void detachTab(tabId, reason) - return - } - - if (tabInfo.url?.startsWith('chrome://') || tabInfo.url?.startsWith('chrome-extension://')) { - void detachTab(tabId, reason) - return - } - - if (reattachPending.has(tabId)) return - - const oldTab = tabs.get(tabId) - const oldSessionId = oldTab?.sessionId - const oldTargetId = oldTab?.targetId - - if (oldSessionId) tabBySession.delete(oldSessionId) - tabs.delete(tabId) - for (const [childSessionId, parentTabId] of childSessionToTab.entries()) { - if (parentTabId === tabId) childSessionToTab.delete(childSessionId) - } - - if (oldSessionId && oldTargetId) { - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.detachedFromTarget', - params: { sessionId: oldSessionId, targetId: oldTargetId, reason: 'navigation-reattach' }, - }, - }) - } catch { - // Relay may be down. - } - } - - reattachPending.add(tabId) - setBadge(tabId, 'connecting') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: re-attaching after navigation…', - }) - - // Extend re-attach window from 2.5 s to ~7.7 s (5 attempts). - // SPAs and pages with heavy JS can take >2.5 s before the Chrome debugger - // is attachable, causing all three original attempts to fail and leaving - // the badge permanently off after every navigation. - const delays = [200, 500, 1000, 2000, 4000] - for (let attempt = 0; attempt < delays.length; attempt++) { - await new Promise((r) => setTimeout(r, delays[attempt])) - - if (!reattachPending.has(tabId)) return - - try { - await chrome.tabs.get(tabId) - } catch { - reattachPending.delete(tabId) - setBadge(tabId, 'off') - return - } - - const relayUp = relayWs && relayWs.readyState === WebSocket.OPEN - - try { - // When relay is down, still attach the debugger but skip sending the - // relay event. reannounceAttachedTabs() will notify the relay once it - // reconnects, so the tab stays tracked across transient relay drops. - await attachTab(tabId, { skipAttachedEvent: !relayUp }) - reattachPending.delete(tabId) - if (!relayUp) { - setBadge(tabId, 'connecting') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: attached, waiting for relay reconnect…', - }) - } - return - } catch { - // continue retries - } - } - - reattachPending.delete(tabId) - setBadge(tabId, 'off') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: re-attach failed (click to retry)', - }) -} - -// Tab lifecycle listeners — clean up stale entries. -chrome.tabs.onRemoved.addListener((tabId) => void whenReady(() => { - reattachPending.delete(tabId) - if (!tabs.has(tabId)) return - const tab = tabs.get(tabId) - if (tab?.sessionId) tabBySession.delete(tab.sessionId) - tabs.delete(tabId) - for (const [childSessionId, parentTabId] of childSessionToTab.entries()) { - if (parentTabId === tabId) childSessionToTab.delete(childSessionId) - } - if (tab?.sessionId && tab?.targetId) { - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.detachedFromTarget', - params: { sessionId: tab.sessionId, targetId: tab.targetId, reason: 'tab_closed' }, - }, - }) - } catch { - // Relay may be down. - } - } - void persistState() -})) - -chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => void whenReady(() => { - const tab = tabs.get(removedTabId) - if (!tab) return - tabs.delete(removedTabId) - tabs.set(addedTabId, tab) - if (tab.sessionId) { - tabBySession.set(tab.sessionId, addedTabId) - } - for (const [childSessionId, parentTabId] of childSessionToTab.entries()) { - if (parentTabId === removedTabId) { - childSessionToTab.set(childSessionId, addedTabId) - } - } - setBadge(addedTabId, 'on') - void persistState() -})) - -// Register debugger listeners at module scope so detach/event handling works -// even when the relay WebSocket is down. -chrome.debugger.onEvent.addListener((...args) => void whenReady(() => onDebuggerEvent(...args))) -chrome.debugger.onDetach.addListener((...args) => void whenReady(() => onDebuggerDetach(...args))) - -chrome.action.onClicked.addListener(() => void whenReady(() => connectOrToggleForActiveTab())) - -// Refresh badge after navigation completes — service worker may have restarted -// during navigation, losing ephemeral badge state. -chrome.webNavigation.onCompleted.addListener(({ tabId, frameId }) => void whenReady(() => { - if (frameId !== 0) return - const tab = tabs.get(tabId) - if (tab?.state === 'connected') { - setBadge(tabId, relayWs && relayWs.readyState === WebSocket.OPEN ? 'on' : 'connecting') - } -})) - -// Refresh badge when user switches to an attached tab. -chrome.tabs.onActivated.addListener(({ tabId }) => void whenReady(() => { - const tab = tabs.get(tabId) - if (tab?.state === 'connected') { - setBadge(tabId, relayWs && relayWs.readyState === WebSocket.OPEN ? 'on' : 'connecting') - } -})) - -chrome.runtime.onInstalled.addListener(() => { - void chrome.runtime.openOptionsPage() -}) - -// MV3 keepalive via chrome.alarms — more reliable than setInterval across -// service worker restarts. Checks relay health and refreshes badges. -chrome.alarms.create('relay-keepalive', { periodInMinutes: 0.5 }) - -chrome.alarms.onAlarm.addListener(async (alarm) => { - if (alarm.name !== 'relay-keepalive') return - await initPromise - - if (tabs.size === 0) return - - // Refresh badges (ephemeral in MV3). - for (const [tabId, tab] of tabs.entries()) { - if (tab.state === 'connected') { - setBadge(tabId, relayWs && relayWs.readyState === WebSocket.OPEN ? 'on' : 'connecting') - } - } - - // If relay is down and no reconnect is in progress, trigger one. - if (!relayWs || relayWs.readyState !== WebSocket.OPEN) { - if (!relayConnectPromise && !reconnectTimer) { - console.log('Keepalive: WebSocket unhealthy, triggering reconnect') - await ensureRelayConnection().catch(() => { - // ensureRelayConnection may throw without triggering onRelayClosed - // (e.g. preflight fetch fails before WS is created), so ensure - // reconnect is always scheduled on failure. - if (!reconnectTimer) { - scheduleReconnect() - } - }) - } - } -}) - -// Rehydrate state on service worker startup. Split: rehydration is the gate -// (fast), relay reconnect runs in background (slow, non-blocking). -const initPromise = rehydrateState() - -initPromise.then(() => { - if (tabs.size > 0) { - ensureRelayConnection().then(() => { - reconnectAttempt = 0 - return reannounceAttachedTabs() - }).catch(() => { - scheduleReconnect() - }) - } -}) - -// Shared gate: all state-dependent handlers await this before accessing maps. -async function whenReady(fn) { - await initPromise - return fn() -} - -// Relay check handler for the options page. The service worker has -// host_permissions and bypasses CORS preflight, so the options page -// delegates token-validation requests here. -chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => { - if (msg?.type !== 'relayCheck') return false - const { url, token } = msg - const headers = token ? { 'x-openclaw-relay-token': token } : {} - fetch(url, { method: 'GET', headers, signal: AbortSignal.timeout(2000) }) - .then(async (res) => { - const contentType = String(res.headers.get('content-type') || '') - let json = null - if (contentType.includes('application/json')) { - try { - json = await res.json() - } catch { - json = null - } - } - sendResponse({ status: res.status, ok: res.ok, contentType, json }) - }) - .catch((err) => sendResponse({ status: 0, ok: false, error: String(err) })) - return true -}) diff --git a/assets/chrome-extension/manifest.json b/assets/chrome-extension/manifest.json deleted file mode 100644 index 62038276cd7..00000000000 --- a/assets/chrome-extension/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "manifest_version": 3, - "name": "OpenClaw Browser Relay", - "version": "0.1.0", - "description": "Attach OpenClaw to your existing Chrome tab via a local CDP relay server.", - "icons": { - "16": "icons/icon16.png", - "32": "icons/icon32.png", - "48": "icons/icon48.png", - "128": "icons/icon128.png" - }, - "permissions": ["debugger", "tabs", "activeTab", "storage", "alarms", "webNavigation"], - "host_permissions": ["http://127.0.0.1/*", "http://localhost/*"], - "background": { "service_worker": "background.js", "type": "module" }, - "action": { - "default_title": "OpenClaw Browser Relay (click to attach/detach)", - "default_icon": { - "16": "icons/icon16.png", - "32": "icons/icon32.png", - "48": "icons/icon48.png", - "128": "icons/icon128.png" - } - }, - "options_ui": { "page": "options.html", "open_in_tab": true } -} diff --git a/assets/chrome-extension/options-validation.js b/assets/chrome-extension/options-validation.js deleted file mode 100644 index 53e2cd55014..00000000000 --- a/assets/chrome-extension/options-validation.js +++ /dev/null @@ -1,57 +0,0 @@ -const PORT_GUIDANCE = 'Use gateway port + 3 (for gateway 18789, relay is 18792).' - -function hasCdpVersionShape(data) { - return !!data && typeof data === 'object' && 'Browser' in data && 'Protocol-Version' in data -} - -export function classifyRelayCheckResponse(res, port) { - if (!res) { - return { action: 'throw', error: 'No response from service worker' } - } - - if (res.status === 401) { - return { action: 'status', kind: 'error', message: 'Gateway token rejected. Check token and save again.' } - } - - if (res.error) { - return { action: 'throw', error: res.error } - } - - if (!res.ok) { - return { action: 'throw', error: `HTTP ${res.status}` } - } - - const contentType = String(res.contentType || '') - if (!contentType.includes('application/json')) { - return { - action: 'status', - kind: 'error', - message: `Wrong port: this is likely the gateway, not the relay. ${PORT_GUIDANCE}`, - } - } - - if (!hasCdpVersionShape(res.json)) { - return { - action: 'status', - kind: 'error', - message: `Wrong port: expected relay /json/version response. ${PORT_GUIDANCE}`, - } - } - - return { action: 'status', kind: 'ok', message: `Relay reachable and authenticated at http://127.0.0.1:${port}/` } -} - -export function classifyRelayCheckException(err, port) { - const message = String(err || '').toLowerCase() - if (message.includes('json') || message.includes('syntax')) { - return { - kind: 'error', - message: `Wrong port: this is not a relay endpoint. ${PORT_GUIDANCE}`, - } - } - - return { - kind: 'error', - message: `Relay not reachable/authenticated at http://127.0.0.1:${port}/. Start OpenClaw browser relay and verify token.`, - } -} diff --git a/assets/chrome-extension/options.html b/assets/chrome-extension/options.html deleted file mode 100644 index 17fc6a79eed..00000000000 --- a/assets/chrome-extension/options.html +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - OpenClaw Browser Relay - - - -
-
- -
-

OpenClaw Browser Relay

-

Click the toolbar button on a tab to attach / detach.

-
-
- -
-
-

Getting started

-

- If you see a red ! badge on the extension icon, the relay server is not reachable. - Start OpenClaw’s browser relay on this machine (Gateway or node host), then click the toolbar button again. -

-

- Full guide (install, remote Gateway, security): docs.openclaw.ai/tools/chrome-extension -

-
- -
-

Relay connection

- -
- -
- -
- - -
-
- Default port: 18792. Extension connects to: http://127.0.0.1:<port>/. - Gateway token must match gateway.auth.token (or OPENCLAW_GATEWAY_TOKEN). -
-
-
-
- - -
- - diff --git a/assets/chrome-extension/options.js b/assets/chrome-extension/options.js deleted file mode 100644 index aa6fcc4901f..00000000000 --- a/assets/chrome-extension/options.js +++ /dev/null @@ -1,74 +0,0 @@ -import { deriveRelayToken } from './background-utils.js' -import { classifyRelayCheckException, classifyRelayCheckResponse } from './options-validation.js' - -const DEFAULT_PORT = 18792 - -function clampPort(value) { - const n = Number.parseInt(String(value || ''), 10) - if (!Number.isFinite(n)) return DEFAULT_PORT - if (n <= 0 || n > 65535) return DEFAULT_PORT - return n -} - -function updateRelayUrl(port) { - const el = document.getElementById('relay-url') - if (!el) return - el.textContent = `http://127.0.0.1:${port}/` -} - -function setStatus(kind, message) { - const status = document.getElementById('status') - if (!status) return - status.dataset.kind = kind || '' - status.textContent = message || '' -} - -async function checkRelayReachable(port, token) { - const url = `http://127.0.0.1:${port}/json/version` - const trimmedToken = String(token || '').trim() - if (!trimmedToken) { - setStatus('error', 'Gateway token required. Save your gateway token to connect.') - return - } - try { - const relayToken = await deriveRelayToken(trimmedToken, port) - // Delegate the fetch to the background service worker to bypass - // CORS preflight on the custom x-openclaw-relay-token header. - const res = await chrome.runtime.sendMessage({ - type: 'relayCheck', - url, - token: relayToken, - }) - const result = classifyRelayCheckResponse(res, port) - if (result.action === 'throw') throw new Error(result.error) - setStatus(result.kind, result.message) - } catch (err) { - const result = classifyRelayCheckException(err, port) - setStatus(result.kind, result.message) - } -} - -async function load() { - const stored = await chrome.storage.local.get(['relayPort', 'gatewayToken']) - const port = clampPort(stored.relayPort) - const token = String(stored.gatewayToken || '').trim() - document.getElementById('port').value = String(port) - document.getElementById('token').value = token - updateRelayUrl(port) - await checkRelayReachable(port, token) -} - -async function save() { - const portInput = document.getElementById('port') - const tokenInput = document.getElementById('token') - const port = clampPort(portInput.value) - const token = String(tokenInput.value || '').trim() - await chrome.storage.local.set({ relayPort: port, gatewayToken: token }) - portInput.value = String(port) - tokenInput.value = token - updateRelayUrl(port) - await checkRelayReachable(port, token) -} - -document.getElementById('save').addEventListener('click', () => void save()) -void load() diff --git a/docs/.generated/config-baseline.json b/docs/.generated/config-baseline.json index dee3827bbcc..bf67b685710 100644 --- a/docs/.generated/config-baseline.json +++ b/docs/.generated/config-baseline.json @@ -8035,21 +8035,7 @@ "storage" ], "label": "Browser Profile Driver", - "help": "Per-profile browser driver mode: \"openclaw\" (or legacy \"clawd\") or \"extension\" depending on connection/runtime strategy. Use the driver that matches your browser control stack to avoid protocol mismatches.", - "hasChildren": false - }, - { - "path": "browser.relayBindHost", - "kind": "core", - "type": "string", - "required": false, - "deprecated": false, - "sensitive": false, - "tags": [ - "advanced" - ], - "label": "Browser Relay Bind Address", - "help": "Bind IP address for the Chrome extension relay listener. Leave unset for loopback-only access, or set an explicit non-loopback IP such as 0.0.0.0 only when the relay must be reachable across network namespaces (for example WSL2) and the surrounding network is already trusted.", + "help": "Per-profile browser driver mode. Use \"openclaw\" (or legacy \"clawd\") for CDP-based profiles, or use \"existing-session\" for host-local Chrome MCP attachment.", "hasChildren": false }, { @@ -40266,6 +40252,22 @@ "help": "Debounce window (ms) before applying config changes.", "hasChildren": false }, + { + "path": "gateway.reload.deferralTimeoutMs", + "kind": "core", + "type": "integer", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "network", + "performance", + "reliability" + ], + "label": "Restart Deferral Timeout (ms)", + "help": "Maximum time (ms) to wait for in-flight operations to complete before forcing a SIGUSR1 restart. Default: 300000 (5 minutes). Lower values risk aborting active subagent LLM calls.", + "hasChildren": false + }, { "path": "gateway.reload.mode", "kind": "core", @@ -44993,6 +44995,75 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.amazon-bedrock", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/amazon-bedrock-provider", + "help": "OpenClaw Amazon Bedrock provider plugin (plugin: amazon-bedrock)", + "hasChildren": true + }, + { + "path": "plugins.entries.amazon-bedrock.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/amazon-bedrock-provider Config", + "help": "Plugin-defined config payload for amazon-bedrock.", + "hasChildren": false + }, + { + "path": "plugins.entries.amazon-bedrock.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/amazon-bedrock-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.amazon-bedrock.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.amazon-bedrock.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.anthropic", "kind": "plugin", @@ -58804,7 +58875,7 @@ "advanced" ], "label": "Setup Wizard State", - "help": "Setup wizard state tracking fields that record the most recent guided onboarding run details. Keep these fields for observability and troubleshooting of setup flows across upgrades.", + "help": "Setup wizard state tracking fields that record the most recent guided setup run details. Keep these fields for observability and troubleshooting of setup flows across upgrades.", "hasChildren": true }, { @@ -58818,7 +58889,7 @@ "advanced" ], "label": "Wizard Last Run Timestamp", - "help": "ISO timestamp for when the setup wizard most recently completed on this host. Use this to confirm onboarding recency during support and operational audits.", + "help": "ISO timestamp for when the setup wizard most recently completed on this host. Use this to confirm setup recency during support and operational audits.", "hasChildren": false }, { @@ -58832,7 +58903,7 @@ "advanced" ], "label": "Wizard Last Run Command", - "help": "Command invocation recorded for the latest wizard run to preserve execution context. Use this to reproduce onboarding steps when verifying setup regressions.", + "help": "Command invocation recorded for the latest wizard run to preserve execution context. Use this to reproduce setup steps when verifying setup regressions.", "hasChildren": false }, { @@ -58846,7 +58917,7 @@ "advanced" ], "label": "Wizard Last Run Commit", - "help": "Source commit identifier recorded for the last wizard execution in development builds. Use this to correlate onboarding behavior with exact source state during debugging.", + "help": "Source commit identifier recorded for the last wizard execution in development builds. Use this to correlate setup behavior with exact source state during debugging.", "hasChildren": false }, { @@ -58874,7 +58945,7 @@ "advanced" ], "label": "Wizard Last Run Version", - "help": "OpenClaw version recorded at the time of the most recent wizard run on this config. Use this when diagnosing behavior differences across version-to-version onboarding changes.", + "help": "OpenClaw version recorded at the time of the most recent wizard run on this config. Use this when diagnosing behavior differences across version-to-version setup changes.", "hasChildren": false } ] diff --git a/docs/.generated/config-baseline.jsonl b/docs/.generated/config-baseline.jsonl index 004f48478bb..34c4f9d5378 100644 --- a/docs/.generated/config-baseline.jsonl +++ b/docs/.generated/config-baseline.jsonl @@ -1,4 +1,4 @@ -{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5093} +{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5098} {"recordType":"path","path":"acp","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"ACP","help":"ACP runtime controls for enabling dispatch, selecting backends, constraining allowed agent targets, and tuning streamed turn projection behavior.","hasChildren":true} {"recordType":"path","path":"acp.allowedAgents","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"ACP Allowed Agents","help":"Allowlist of ACP target agent ids permitted for ACP runtime sessions. Empty means no additional allowlist restriction.","hasChildren":true} {"recordType":"path","path":"acp.allowedAgents.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} @@ -707,8 +707,7 @@ {"recordType":"path","path":"browser.profiles.*.cdpPort","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Browser Profile CDP Port","help":"Per-profile local CDP port used when connecting to browser instances by port instead of URL. Use unique ports per profile to avoid connection collisions.","hasChildren":false} {"recordType":"path","path":"browser.profiles.*.cdpUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Browser Profile CDP URL","help":"Per-profile CDP websocket URL used for explicit remote browser routing by profile name. Use this when profile connections terminate on remote hosts or tunnels.","hasChildren":false} {"recordType":"path","path":"browser.profiles.*.color","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Browser Profile Accent Color","help":"Per-profile accent color for visual differentiation in dashboards and browser-related UI hints. Use distinct colors for high-signal operator recognition of active profiles.","hasChildren":false} -{"recordType":"path","path":"browser.profiles.*.driver","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Browser Profile Driver","help":"Per-profile browser driver mode: \"openclaw\" (or legacy \"clawd\") or \"extension\" depending on connection/runtime strategy. Use the driver that matches your browser control stack to avoid protocol mismatches.","hasChildren":false} -{"recordType":"path","path":"browser.relayBindHost","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Browser Relay Bind Address","help":"Bind IP address for the Chrome extension relay listener. Leave unset for loopback-only access, or set an explicit non-loopback IP such as 0.0.0.0 only when the relay must be reachable across network namespaces (for example WSL2) and the surrounding network is already trusted.","hasChildren":false} +{"recordType":"path","path":"browser.profiles.*.driver","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Browser Profile Driver","help":"Per-profile browser driver mode. Use \"openclaw\" (or legacy \"clawd\") for CDP-based profiles, or use \"existing-session\" for host-local Chrome MCP attachment.","hasChildren":false} {"recordType":"path","path":"browser.remoteCdpHandshakeTimeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Remote CDP Handshake Timeout (ms)","help":"Timeout in milliseconds for post-connect CDP handshake readiness checks against remote browser targets. Raise this for slow-start remote browsers and lower to fail fast in automation loops.","hasChildren":false} {"recordType":"path","path":"browser.remoteCdpTimeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Remote CDP Timeout (ms)","help":"Timeout in milliseconds for connecting to a remote CDP endpoint before failing the browser attach attempt. Increase for high-latency tunnels, or lower for faster failure detection.","hasChildren":false} {"recordType":"path","path":"browser.snapshotDefaults","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Browser Snapshot Defaults","help":"Default snapshot capture configuration used when callers do not provide explicit snapshot options. Tune this for consistent capture behavior across channels and automation paths.","hasChildren":true} @@ -3604,6 +3603,7 @@ {"recordType":"path","path":"gateway.push.apns.relay.timeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["network","performance"],"label":"Gateway APNs Relay Timeout (ms)","help":"Timeout in milliseconds for relay send requests from the gateway to the APNs relay (default: 10000). Increase for slower relays or networks, or lower to fail wake attempts faster.","hasChildren":false} {"recordType":"path","path":"gateway.reload","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["network","reliability"],"label":"Config Reload","help":"Live config-reload policy for how edits are applied and when full restarts are triggered. Keep hybrid behavior for safest operational updates unless debugging reload internals.","hasChildren":true} {"recordType":"path","path":"gateway.reload.debounceMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["network","performance","reliability"],"label":"Config Reload Debounce (ms)","help":"Debounce window (ms) before applying config changes.","hasChildren":false} +{"recordType":"path","path":"gateway.reload.deferralTimeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["network","performance","reliability"],"label":"Restart Deferral Timeout (ms)","help":"Maximum time (ms) to wait for in-flight operations to complete before forcing a SIGUSR1 restart. Default: 300000 (5 minutes). Lower values risk aborting active subagent LLM calls.","hasChildren":false} {"recordType":"path","path":"gateway.reload.mode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["network","reliability"],"label":"Config Reload Mode","help":"Controls how config edits are applied: \"off\" ignores live edits, \"restart\" always restarts, \"hot\" applies in-process, and \"hybrid\" tries hot then restarts if required. Keep \"hybrid\" for safest routine updates.","hasChildren":false} {"recordType":"path","path":"gateway.remote","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["network"],"label":"Remote Gateway","help":"Remote gateway connection settings for direct or SSH transport when this instance proxies to another runtime host. Use remote mode only when split-host operation is intentionally configured.","hasChildren":true} {"recordType":"path","path":"gateway.remote.password","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","network","security"],"label":"Remote Gateway Password","help":"Password credential used for remote gateway authentication when password mode is enabled. Keep this secret managed externally and avoid plaintext values in committed config.","hasChildren":true} @@ -3986,6 +3986,11 @@ {"recordType":"path","path":"plugins.entries.acpx.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable ACPX Runtime","hasChildren":false} {"recordType":"path","path":"plugins.entries.acpx.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.acpx.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.amazon-bedrock","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/amazon-bedrock-provider","help":"OpenClaw Amazon Bedrock provider plugin (plugin: amazon-bedrock)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.amazon-bedrock.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/amazon-bedrock-provider Config","help":"Plugin-defined config payload for amazon-bedrock.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.amazon-bedrock.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/amazon-bedrock-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.amazon-bedrock.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.amazon-bedrock.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.anthropic","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/anthropic-provider","help":"OpenClaw Anthropic provider plugin (plugin: anthropic)","hasChildren":true} {"recordType":"path","path":"plugins.entries.anthropic.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/anthropic-provider Config","help":"Plugin-defined config payload for anthropic.","hasChildren":false} {"recordType":"path","path":"plugins.entries.anthropic.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/anthropic-provider","hasChildren":false} @@ -5086,9 +5091,9 @@ {"recordType":"path","path":"web.reconnect.jitter","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Web Reconnect Jitter","help":"Randomization factor (0-1) applied to reconnect delays to desynchronize clients after outage events. Keep non-zero jitter in multi-client deployments to reduce synchronized spikes.","hasChildren":false} {"recordType":"path","path":"web.reconnect.maxAttempts","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Web Reconnect Max Attempts","help":"Maximum reconnect attempts before giving up for the current failure sequence (0 means no retries). Use finite caps for controlled failure handling in automation-sensitive environments.","hasChildren":false} {"recordType":"path","path":"web.reconnect.maxMs","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Web Reconnect Max Delay (ms)","help":"Maximum reconnect backoff cap in milliseconds to bound retry delay growth over repeated failures. Use a reasonable cap so recovery remains timely after prolonged outages.","hasChildren":false} -{"recordType":"path","path":"wizard","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Setup Wizard State","help":"Setup wizard state tracking fields that record the most recent guided onboarding run details. Keep these fields for observability and troubleshooting of setup flows across upgrades.","hasChildren":true} -{"recordType":"path","path":"wizard.lastRunAt","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Timestamp","help":"ISO timestamp for when the setup wizard most recently completed on this host. Use this to confirm onboarding recency during support and operational audits.","hasChildren":false} -{"recordType":"path","path":"wizard.lastRunCommand","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Command","help":"Command invocation recorded for the latest wizard run to preserve execution context. Use this to reproduce onboarding steps when verifying setup regressions.","hasChildren":false} -{"recordType":"path","path":"wizard.lastRunCommit","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Commit","help":"Source commit identifier recorded for the last wizard execution in development builds. Use this to correlate onboarding behavior with exact source state during debugging.","hasChildren":false} +{"recordType":"path","path":"wizard","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Setup Wizard State","help":"Setup wizard state tracking fields that record the most recent guided setup run details. Keep these fields for observability and troubleshooting of setup flows across upgrades.","hasChildren":true} +{"recordType":"path","path":"wizard.lastRunAt","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Timestamp","help":"ISO timestamp for when the setup wizard most recently completed on this host. Use this to confirm setup recency during support and operational audits.","hasChildren":false} +{"recordType":"path","path":"wizard.lastRunCommand","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Command","help":"Command invocation recorded for the latest wizard run to preserve execution context. Use this to reproduce setup steps when verifying setup regressions.","hasChildren":false} +{"recordType":"path","path":"wizard.lastRunCommit","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Commit","help":"Source commit identifier recorded for the last wizard execution in development builds. Use this to correlate setup behavior with exact source state during debugging.","hasChildren":false} {"recordType":"path","path":"wizard.lastRunMode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Mode","help":"Wizard execution mode recorded as \"local\" or \"remote\" for the most recent setup flow. Use this to understand whether setup targeted direct local runtime or remote gateway topology.","hasChildren":false} -{"recordType":"path","path":"wizard.lastRunVersion","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Version","help":"OpenClaw version recorded at the time of the most recent wizard run on this config. Use this when diagnosing behavior differences across version-to-version onboarding changes.","hasChildren":false} +{"recordType":"path","path":"wizard.lastRunVersion","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Version","help":"OpenClaw version recorded at the time of the most recent wizard run on this config. Use this when diagnosing behavior differences across version-to-version setup changes.","hasChildren":false} diff --git a/docs/.i18n/glossary.zh-CN.json b/docs/.i18n/glossary.zh-CN.json index bc1892d1e9a..36e44b6d909 100644 --- a/docs/.i18n/glossary.zh-CN.json +++ b/docs/.i18n/glossary.zh-CN.json @@ -55,10 +55,6 @@ "source": "CLI Setup Reference", "target": "CLI 设置参考" }, - { - "source": "Setup Overview", - "target": "设置概览" - }, { "source": "Setup Wizard (CLI)", "target": "设置向导(CLI)" diff --git a/docs/automation/hooks.md b/docs/automation/hooks.md index 84c7a234e11..deda79d3db5 100644 --- a/docs/automation/hooks.md +++ b/docs/automation/hooks.md @@ -74,7 +74,7 @@ openclaw hooks info session-memory ### Onboarding -During onboarding (`openclaw setup --wizard`), you'll be prompted to enable recommended hooks. The wizard automatically discovers eligible hooks and presents them for selection. +During onboarding (`openclaw onboard`), you'll be prompted to enable recommended hooks. The wizard automatically discovers eligible hooks and presents them for selection. ## Hook Discovery diff --git a/docs/channels/bluebubbles.md b/docs/channels/bluebubbles.md index c51c7967b00..9c2f0eb6de4 100644 --- a/docs/channels/bluebubbles.md +++ b/docs/channels/bluebubbles.md @@ -26,7 +26,7 @@ Status: bundled plugin that talks to the BlueBubbles macOS server over HTTP. **R 1. Install the BlueBubbles server on your Mac (follow the instructions at [bluebubbles.app/install](https://bluebubbles.app/install)). 2. In the BlueBubbles config, enable the web API and set a password. -3. Run `openclaw setup --wizard` and select BlueBubbles, or configure manually: +3. Run `openclaw onboard` and select BlueBubbles, or configure manually: ```json5 { @@ -129,7 +129,7 @@ launchctl load ~/Library/LaunchAgents/com.user.poke-messages.plist BlueBubbles is available in the interactive setup wizard: ``` -openclaw setup --wizard +openclaw onboard ``` The wizard prompts for: diff --git a/docs/channels/feishu.md b/docs/channels/feishu.md index 7e13a3077df..41882e78264 100644 --- a/docs/channels/feishu.md +++ b/docs/channels/feishu.md @@ -35,7 +35,7 @@ There are two ways to add the Feishu channel: If you just installed OpenClaw, run the setup wizard: ```bash -openclaw setup --wizard +openclaw onboard ``` The wizard guides you through: @@ -711,7 +711,7 @@ Key options: - ✅ Images - ✅ Files - ✅ Audio -- ✅ Video +- ✅ Video/media - ✅ Stickers ### Send @@ -720,4 +720,28 @@ Key options: - ✅ Images - ✅ Files - ✅ Audio -- ⚠️ Rich text (partial support) +- ✅ Video/media +- ✅ Interactive cards +- ⚠️ Rich text (post-style formatting and cards, not arbitrary Feishu authoring features) + +### Threads and replies + +- ✅ Inline replies +- ✅ Topic-thread replies where Feishu exposes `reply_in_thread` +- ✅ Media replies stay thread-aware when replying to a thread/topic message + +## Runtime action surface + +Feishu currently exposes these runtime actions: + +- `send` +- `read` +- `edit` +- `thread-reply` +- `pin` +- `list-pins` +- `unpin` +- `member-info` +- `channel-info` +- `channel-list` +- `react` and `reactions` when reactions are enabled in config diff --git a/docs/channels/nostr.md b/docs/channels/nostr.md index ce410dd879a..46888da0352 100644 --- a/docs/channels/nostr.md +++ b/docs/channels/nostr.md @@ -16,7 +16,7 @@ Nostr is a decentralized protocol for social networking. This channel enables Op ### Onboarding (recommended) -- The setup wizard (`openclaw setup --wizard`) and `openclaw channels add` list optional channel plugins. +- The setup wizard (`openclaw onboard`) and `openclaw channels add` list optional channel plugins. - Selecting Nostr prompts you to install the plugin on demand. Install defaults: diff --git a/docs/channels/synology-chat.md b/docs/channels/synology-chat.md index cc3b2f2ed73..aae655f27b7 100644 --- a/docs/channels/synology-chat.md +++ b/docs/channels/synology-chat.md @@ -27,7 +27,7 @@ Details: [Plugins](/tools/plugin) ## Quick setup 1. Install and enable the Synology Chat plugin. - - `openclaw setup --wizard` now shows Synology Chat in the same channel setup list as `openclaw channels add`. + - `openclaw onboard` now shows Synology Chat in the same channel setup list as `openclaw channels add`. - Non-interactive setup: `openclaw channels add --channel synology-chat --token --url ` 2. In Synology Chat integrations: - Create an incoming webhook and copy its URL. @@ -36,7 +36,7 @@ Details: [Plugins](/tools/plugin) - `https://gateway-host/webhook/synology` by default. - Or your custom `channels.synology-chat.webhookPath`. 4. Finish setup in OpenClaw. - - Guided: `openclaw setup --wizard` + - Guided: `openclaw onboard` - Direct: `openclaw channels add --channel synology-chat --token --url ` 5. Restart gateway and send a DM to the Synology Chat bot. diff --git a/docs/cli/browser.md b/docs/cli/browser.md index f9ddc151717..42af08f84f3 100644 --- a/docs/cli/browser.md +++ b/docs/cli/browser.md @@ -1,9 +1,9 @@ --- -summary: "CLI reference for `openclaw browser` (profiles, tabs, actions, extension relay)" +summary: "CLI reference for `openclaw browser` (profiles, tabs, actions, Chrome MCP, and CDP)" read_when: - You use `openclaw browser` and want examples for common tasks - You want to control a browser running on another machine via a node host - - You want to use the Chrome extension relay (attach/detach via toolbar button) + - You want to attach to your local signed-in Chrome via Chrome MCP title: "browser" --- @@ -14,7 +14,6 @@ Manage OpenClaw’s browser control server and run browser actions (tabs, snapsh Related: - Browser tool + API: [Browser tool](/tools/browser) -- Chrome extension relay: [Chrome extension](/tools/chrome-extension) ## Common flags @@ -37,13 +36,14 @@ openclaw browser --browser-profile openclaw snapshot Profiles are named browser routing configs. In practice: -- `openclaw`: launches/attaches to a dedicated OpenClaw-managed Chrome instance (isolated user data dir). +- `openclaw`: launches or attaches to a dedicated OpenClaw-managed Chrome instance (isolated user data dir). - `user`: controls your existing signed-in Chrome session via Chrome DevTools MCP. -- `chrome-relay`: controls your existing Chrome tab(s) via the Chrome extension relay. +- custom CDP profiles: point at a local or remote CDP endpoint. ```bash openclaw browser profiles openclaw browser create-profile --name work --color "#FF5A36" +openclaw browser create-profile --name chrome-live --driver existing-session openclaw browser delete-profile --name work ``` @@ -84,20 +84,17 @@ openclaw browser click openclaw browser type "hello" ``` -## Chrome extension relay (attach via toolbar button) +## Existing Chrome via MCP -This mode lets the agent control an existing Chrome tab that you attach manually (it does not auto-attach). - -Install the unpacked extension to a stable path: +Use the built-in `user` profile, or create your own `existing-session` profile: ```bash -openclaw browser extension install -openclaw browser extension path +openclaw browser --browser-profile user tabs +openclaw browser create-profile --name chrome-live --driver existing-session +openclaw browser --browser-profile chrome-live tabs ``` -Then Chrome → `chrome://extensions` → enable “Developer mode” → “Load unpacked” → select the printed folder. - -Full guide: [Chrome extension](/tools/chrome-extension) +This path is host-only. For Docker, headless servers, Browserless, or other remote setups, use a CDP profile instead. ## Remote browser control (node host proxy) diff --git a/docs/cli/docs.md b/docs/cli/docs.md index 6b79aabe6f1..744c50e1432 100644 --- a/docs/cli/docs.md +++ b/docs/cli/docs.md @@ -10,6 +10,6 @@ title: "docs" Search the live docs index. ```bash -openclaw docs browser extension +openclaw docs browser existing-session openclaw docs sandbox allowHostControl ``` diff --git a/docs/cli/index.md b/docs/cli/index.md index 80e6efdadd5..ded970cde9d 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -13,7 +13,7 @@ This page describes the current CLI behavior. If commands change, update this do ## Command pages - [`setup`](/cli/setup) -- [`onboard`](/cli/onboard) (legacy alias for `setup --wizard`) +- [`onboard`](/cli/onboard) - [`configure`](/cli/configure) - [`config`](/cli/config) - [`completion`](/cli/completion) diff --git a/docs/cli/onboard.md b/docs/cli/onboard.md index 16aa8413135..899ccd82713 100644 --- a/docs/cli/onboard.md +++ b/docs/cli/onboard.md @@ -1,30 +1,157 @@ --- -summary: "Legacy CLI alias for `openclaw setup --wizard`" +summary: "CLI reference for `openclaw onboard` (interactive setup wizard)" read_when: - - You encountered `openclaw onboard` in older docs or scripts + - You want guided setup for gateway, workspace, auth, channels, and skills title: "onboard" --- # `openclaw onboard` -Legacy alias for `openclaw setup --wizard`. - -Prefer: - -```bash -openclaw setup --wizard -``` - -`openclaw onboard` still accepts the same flags and behavior for compatibility. +Interactive setup wizard (local or remote Gateway setup). ## Related guides -- Primary command docs: [`openclaw setup`](/cli/setup) -- Setup wizard guide: [Setup Wizard (CLI)](/start/wizard) -- Setup overview: [Setup Overview](/start/onboarding-overview) -- Setup wizard reference: [CLI Setup Reference](/start/wizard-cli-reference) +- CLI onboarding hub: [Setup Wizard (CLI)](/start/wizard) +- Onboarding overview: [Onboarding Overview](/start/onboarding-overview) +- CLI onboarding reference: [CLI Setup Reference](/start/wizard-cli-reference) - CLI automation: [CLI Automation](/start/wizard-cli-automation) - macOS onboarding: [Onboarding (macOS App)](/start/onboarding) -For examples, flags, and non-interactive behavior, use the primary docs at -[`openclaw setup`](/cli/setup) and [CLI Setup Reference](/start/wizard-cli-reference). +## Examples + +```bash +openclaw onboard +openclaw onboard --flow quickstart +openclaw onboard --flow manual +openclaw onboard --mode remote --remote-url wss://gateway-host:18789 +``` + +For plaintext private-network `ws://` targets (trusted networks only), set +`OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1` in the onboarding process environment. + +Non-interactive custom provider: + +```bash +openclaw onboard --non-interactive \ + --auth-choice custom-api-key \ + --custom-base-url "https://llm.example.com/v1" \ + --custom-model-id "foo-large" \ + --custom-api-key "$CUSTOM_API_KEY" \ + --secret-input-mode plaintext \ + --custom-compatibility openai +``` + +`--custom-api-key` is optional in non-interactive mode. If omitted, onboarding checks `CUSTOM_API_KEY`. + +Non-interactive Ollama: + +```bash +openclaw onboard --non-interactive \ + --auth-choice ollama \ + --custom-base-url "http://ollama-host:11434" \ + --custom-model-id "qwen3.5:27b" \ + --accept-risk +``` + +`--custom-base-url` defaults to `http://127.0.0.1:11434`. `--custom-model-id` is optional; if omitted, onboarding uses Ollama's suggested defaults. Cloud model IDs such as `kimi-k2.5:cloud` also work here. + +Store provider keys as refs instead of plaintext: + +```bash +openclaw onboard --non-interactive \ + --auth-choice openai-api-key \ + --secret-input-mode ref \ + --accept-risk +``` + +With `--secret-input-mode ref`, onboarding writes env-backed refs instead of plaintext key values. +For auth-profile backed providers this writes `keyRef` entries; for custom providers this writes `models.providers..apiKey` as an env ref (for example `{ source: "env", provider: "default", id: "CUSTOM_API_KEY" }`). + +Non-interactive `ref` mode contract: + +- Set the provider env var in the onboarding process environment (for example `OPENAI_API_KEY`). +- Do not pass inline key flags (for example `--openai-api-key`) unless that env var is also set. +- If an inline key flag is passed without the required env var, onboarding fails fast with guidance. + +Gateway token options in non-interactive mode: + +- `--gateway-auth token --gateway-token ` stores a plaintext token. +- `--gateway-auth token --gateway-token-ref-env ` stores `gateway.auth.token` as an env SecretRef. +- `--gateway-token` and `--gateway-token-ref-env` are mutually exclusive. +- `--gateway-token-ref-env` requires a non-empty env var in the onboarding process environment. +- With `--install-daemon`, when token auth requires a token, SecretRef-managed gateway tokens are validated but not persisted as resolved plaintext in supervisor service environment metadata. +- With `--install-daemon`, if token mode requires a token and the configured token SecretRef is unresolved, onboarding fails closed with remediation guidance. +- With `--install-daemon`, if both `gateway.auth.token` and `gateway.auth.password` are configured and `gateway.auth.mode` is unset, onboarding blocks install until mode is set explicitly. + +Example: + +```bash +export OPENCLAW_GATEWAY_TOKEN="your-token" +openclaw onboard --non-interactive \ + --mode local \ + --auth-choice skip \ + --gateway-auth token \ + --gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN \ + --accept-risk +``` + +Non-interactive local gateway health: + +- Unless you pass `--skip-health`, onboarding waits for a reachable local gateway before it exits successfully. +- `--install-daemon` starts the managed gateway install path first. Without it, you must already have a local gateway running, for example `openclaw gateway run`. +- If you only want config/workspace/bootstrap writes in automation, use `--skip-health`. +- On native Windows, `--install-daemon` tries Scheduled Tasks first and falls back to a per-user Startup-folder login item if task creation is denied. + +Interactive onboarding behavior with reference mode: + +- Choose **Use secret reference** when prompted. +- Then choose either: + - Environment variable + - Configured secret provider (`file` or `exec`) +- Onboarding performs a fast preflight validation before saving the ref. + - If validation fails, onboarding shows the error and lets you retry. + +Non-interactive Z.AI endpoint choices: + +Note: `--auth-choice zai-api-key` now auto-detects the best Z.AI endpoint for your key (prefers the general API with `zai/glm-5`). +If you specifically want the GLM Coding Plan endpoints, pick `zai-coding-global` or `zai-coding-cn`. + +```bash +# Promptless endpoint selection +openclaw onboard --non-interactive \ + --auth-choice zai-coding-global \ + --zai-api-key "$ZAI_API_KEY" + +# Other Z.AI endpoint choices: +# --auth-choice zai-coding-cn +# --auth-choice zai-global +# --auth-choice zai-cn +``` + +Non-interactive Mistral example: + +```bash +openclaw onboard --non-interactive \ + --auth-choice mistral-api-key \ + --mistral-api-key "$MISTRAL_API_KEY" +``` + +Flow notes: + +- `quickstart`: minimal prompts, auto-generates a gateway token. +- `manual`: full prompts for port/bind/auth (alias of `advanced`). +- Local onboarding DM scope behavior: [CLI Setup Reference](/start/wizard-cli-reference#outputs-and-internals). +- Fastest first chat: `openclaw dashboard` (Control UI, no channel setup). +- Custom Provider: connect any OpenAI or Anthropic compatible endpoint, + including hosted providers not listed. Use Unknown to auto-detect. + +## Common follow-up commands + +```bash +openclaw configure +openclaw agents add +``` + + +`--json` does not imply non-interactive mode. Use `--non-interactive` for scripts. + diff --git a/docs/cli/setup.md b/docs/cli/setup.md index d8b5f686ef9..d8992ba8a43 100644 --- a/docs/cli/setup.md +++ b/docs/cli/setup.md @@ -1,43 +1,29 @@ --- -summary: "CLI reference for `openclaw setup` (initialize config/workspace or run the setup wizard)" +summary: "CLI reference for `openclaw setup` (initialize config + workspace)" read_when: - - You want first-run setup without the guided wizard - - You want the guided setup wizard via `openclaw setup --wizard` + - You’re doing first-run setup without the full setup wizard - You want to set the default workspace path title: "setup" --- # `openclaw setup` -Initialize `~/.openclaw/openclaw.json` and the agent workspace, or run the guided setup wizard. +Initialize `~/.openclaw/openclaw.json` and the agent workspace. Related: - Getting started: [Getting started](/start/getting-started) -- Setup wizard: [Setup Wizard (CLI)](/start/wizard) -- macOS app onboarding: [Onboarding](/start/onboarding) +- Wizard: [Onboarding](/start/onboarding) ## Examples ```bash openclaw setup openclaw setup --workspace ~/.openclaw/workspace -openclaw setup --wizard -openclaw setup --wizard --install-daemon ``` -Without flags, `openclaw setup` only ensures config + workspace defaults. -Use `--wizard` for the full guided flow. +To run the wizard via setup: -## Modes - -- `openclaw setup`: initialize config/workspace defaults only -- `openclaw setup --wizard`: guided setup for auth, gateway, channels, and skills -- `openclaw setup --wizard --non-interactive`: scripted setup flow - -## Related guides - -- Setup wizard guide: [Setup Wizard (CLI)](/start/wizard) -- Setup wizard reference: [CLI Setup Reference](/start/wizard-cli-reference) -- Setup wizard automation: [CLI Automation](/start/wizard-cli-automation) -- Legacy alias: [`openclaw onboard`](/cli/onboard) +```bash +openclaw setup --wizard +``` diff --git a/docs/concepts/agent-workspace.md b/docs/concepts/agent-workspace.md index 7fc114818cb..ff55f241bcd 100644 --- a/docs/concepts/agent-workspace.md +++ b/docs/concepts/agent-workspace.md @@ -36,7 +36,7 @@ inside a sandbox workspace under `~/.openclaw/sandboxes`, not your host workspac } ``` -`openclaw setup --wizard`, `openclaw configure`, or `openclaw setup` will create the +`openclaw onboard`, `openclaw configure`, or `openclaw setup` will create the workspace and seed the bootstrap files if they are missing. Sandbox seed copies only accept regular in-workspace files; symlink/hardlink aliases that resolve outside the source workspace are ignored. diff --git a/docs/concepts/model-providers.md b/docs/concepts/model-providers.md index 8767d108132..6adbb5d0f26 100644 --- a/docs/concepts/model-providers.md +++ b/docs/concepts/model-providers.md @@ -15,7 +15,7 @@ For model selection rules, see [/concepts/models](/concepts/models). - Model refs use `provider/model` (example: `opencode/claude-opus-4-6`). - If you set `agents.defaults.models`, it becomes the allowlist. -- CLI helpers: `openclaw setup --wizard`, `openclaw models list`, `openclaw models set `. +- CLI helpers: `openclaw onboard`, `openclaw models list`, `openclaw models set `. - Provider plugins can inject model catalogs via `registerProvider({ catalog })`; OpenClaw merges that output into `models.providers` before writing `models.json`. @@ -25,7 +25,8 @@ For model selection rules, see [/concepts/models](/concepts/models). cases such as Anthropic API-key-first onboarding. - Provider plugins can also own provider runtime behavior via `resolveDynamicModel`, `prepareDynamicModel`, `normalizeResolvedModel`, - `capabilities`, `prepareExtraParams`, `wrapStreamFn`, + `capabilities`, `prepareExtraParams`, `wrapStreamFn`, `formatApiKey`, + `refreshOAuth`, `buildAuthDoctorHint`, `isCacheTtlEligible`, `buildMissingAuthMessage`, `suppressBuiltInModel`, `augmentModelCatalog`, `isBinaryThinking`, `supportsXHighThinking`, `resolveDefaultThinkingLevel`, @@ -41,8 +42,8 @@ Typical split: - `auth[].run` / `auth[].runNonInteractive`: provider owns onboarding/login flows for `openclaw onboard`, `openclaw models auth`, and headless setup -- `wizard.onboarding` / `wizard.modelPicker`: provider owns auth-choice labels, - hints, and setup entries in onboarding/model pickers +- `wizard.setup` / `wizard.modelPicker`: provider owns auth-choice labels, + legacy aliases, onboarding allowlist hints, and setup entries in onboarding/model pickers - `catalog`: provider appears in `models.providers` - `resolveDynamicModel`: provider accepts model ids not present in the local static catalog yet @@ -52,6 +53,12 @@ Typical split: - `capabilities`: provider publishes transcript/tooling/provider-family quirks - `prepareExtraParams`: provider defaults or normalizes per-model request params - `wrapStreamFn`: provider applies request headers/body/model compat wrappers +- `formatApiKey`: provider formats stored auth profiles into the runtime + `apiKey` string expected by the transport +- `refreshOAuth`: provider owns OAuth refresh when the shared `pi-ai` + refreshers are not enough +- `buildAuthDoctorHint`: provider appends repair guidance when OAuth refresh + fails - `isCacheTtlEligible`: provider decides which upstream model ids support prompt-cache TTL - `buildMissingAuthMessage`: provider replaces the generic auth-store error with a provider-specific recovery hint @@ -73,19 +80,21 @@ Typical split: Current bundled examples: -- `anthropic`: Claude 4.6 forward-compat fallback, usage endpoint fetching, - and cache-TTL/provider-family metadata +- `anthropic`: Claude 4.6 forward-compat fallback, auth repair hints, usage + endpoint fetching, and cache-TTL/provider-family metadata - `openrouter`: pass-through model ids, request wrappers, provider capability hints, and cache-TTL policy -- `github-copilot`: forward-compat model fallback, Claude-thinking transcript - hints, runtime token exchange, and usage endpoint fetching +- `github-copilot`: onboarding/device login, forward-compat model fallback, + Claude-thinking transcript hints, runtime token exchange, and usage endpoint + fetching - `openai`: GPT-5.4 forward-compat fallback, direct OpenAI transport normalization, Codex-aware missing-auth hints, Spark suppression, synthetic OpenAI/Codex catalog rows, thinking/live-model policy, and provider-family metadata - `google` and `google-gemini-cli`: Gemini 3.1 forward-compat fallback and - modern-model matching; Gemini CLI OAuth also owns usage-token parsing and - quota endpoint fetching for usage surfaces + modern-model matching; Gemini CLI OAuth also owns auth-profile token + formatting, usage-token parsing, and quota endpoint fetching for usage + surfaces - `moonshot`: shared transport, plugin-owned thinking payload normalization - `kilocode`: shared transport, plugin-owned request headers, reasoning payload normalization, Gemini transcript hints, and cache-TTL policy @@ -93,9 +102,9 @@ Current bundled examples: policy, binary-thinking/live-model policy, and usage auth + quota fetching - `mistral`, `opencode`, and `opencode-go`: plugin-owned capability metadata - `byteplus`, `cloudflare-ai-gateway`, `huggingface`, `kimi-coding`, - `minimax-portal`, `modelstudio`, `nvidia`, `qianfan`, `qwen-portal`, - `synthetic`, `together`, `venice`, `vercel-ai-gateway`, and `volcengine`: - plugin-owned catalogs only + `modelstudio`, `nvidia`, `qianfan`, `synthetic`, `together`, `venice`, + `vercel-ai-gateway`, and `volcengine`: plugin-owned catalogs only +- `qwen-portal`: plugin-owned catalog, OAuth login, and OAuth refresh - `minimax` and `xiaomi`: plugin-owned catalogs plus usage auth/snapshot logic The bundled `openai` plugin now owns both provider ids: `openai` and @@ -130,7 +139,7 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no** - Auth: `OPENAI_API_KEY` - Optional rotation: `OPENAI_API_KEYS`, `OPENAI_API_KEY_1`, `OPENAI_API_KEY_2`, plus `OPENCLAW_LIVE_OPENAI_KEY` (single override) - Example models: `openai/gpt-5.4`, `openai/gpt-5.4-pro` -- CLI: `openclaw setup --wizard --auth-choice openai-api-key` +- CLI: `openclaw onboard --auth-choice openai-api-key` - Default transport is `auto` (WebSocket-first, SSE fallback) - Override per model via `agents.defaults.models["openai/"].params.transport` (`"sse"`, `"websocket"`, or `"auto"`) - OpenAI Responses WebSocket warm-up defaults to enabled via `params.openaiWsWarmup` (`true`/`false`) @@ -150,7 +159,7 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no** - Auth: `ANTHROPIC_API_KEY` or `claude setup-token` - Optional rotation: `ANTHROPIC_API_KEYS`, `ANTHROPIC_API_KEY_1`, `ANTHROPIC_API_KEY_2`, plus `OPENCLAW_LIVE_ANTHROPIC_KEY` (single override) - Example model: `anthropic/claude-opus-4-6` -- CLI: `openclaw setup --wizard --auth-choice token` (paste setup-token) or `openclaw models auth paste-token --provider anthropic` +- CLI: `openclaw onboard --auth-choice token` (paste setup-token) or `openclaw models auth paste-token --provider anthropic` - Direct API-key models support the shared `/fast` toggle and `params.fastMode`; OpenClaw maps that to Anthropic `service_tier` (`auto` vs `standard_only`) - Policy note: setup-token support is technical compatibility; Anthropic has blocked some subscription usage outside Claude Code in the past. Verify current Anthropic terms and decide based on your risk tolerance. - Recommendation: Anthropic API key auth is the safer, recommended path over subscription setup-token auth. @@ -166,7 +175,7 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no** - Provider: `openai-codex` - Auth: OAuth (ChatGPT) - Example model: `openai-codex/gpt-5.4` -- CLI: `openclaw setup --wizard --auth-choice openai-codex` or `openclaw models auth login --provider openai-codex` +- CLI: `openclaw onboard --auth-choice openai-codex` or `openclaw models auth login --provider openai-codex` - Default transport is `auto` (WebSocket-first, SSE fallback) - Override per model via `agents.defaults.models["openai-codex/"].params.transport` (`"sse"`, `"websocket"`, or `"auto"`) - Shares the same `/fast` toggle and `params.fastMode` config as direct `openai/*` @@ -185,7 +194,7 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no** - Zen runtime provider: `opencode` - Go runtime provider: `opencode-go` - Example models: `opencode/claude-opus-4-6`, `opencode-go/kimi-k2.5` -- CLI: `openclaw setup --wizard --auth-choice opencode-zen` or `openclaw setup --wizard --auth-choice opencode-go` +- CLI: `openclaw onboard --auth-choice opencode-zen` or `openclaw onboard --auth-choice opencode-go` ```json5 { @@ -200,7 +209,7 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no** - Optional rotation: `GEMINI_API_KEYS`, `GEMINI_API_KEY_1`, `GEMINI_API_KEY_2`, `GOOGLE_API_KEY` fallback, and `OPENCLAW_LIVE_GEMINI_KEY` (single override) - Example models: `google/gemini-3.1-pro-preview`, `google/gemini-3-flash-preview` - Compatibility: legacy OpenClaw config using `google/gemini-3.1-flash-preview` is normalized to `google/gemini-3-flash-preview` -- CLI: `openclaw setup --wizard --auth-choice gemini-api-key` +- CLI: `openclaw onboard --auth-choice gemini-api-key` ### Google Vertex and Gemini CLI @@ -218,7 +227,7 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no** - Provider: `zai` - Auth: `ZAI_API_KEY` - Example model: `zai/glm-5` -- CLI: `openclaw setup --wizard --auth-choice zai-api-key` +- CLI: `openclaw onboard --auth-choice zai-api-key` - Aliases: `z.ai/*` and `z-ai/*` normalize to `zai/*` ### Vercel AI Gateway @@ -226,14 +235,14 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no** - Provider: `vercel-ai-gateway` - Auth: `AI_GATEWAY_API_KEY` - Example model: `vercel-ai-gateway/anthropic/claude-opus-4.6` -- CLI: `openclaw setup --wizard --auth-choice ai-gateway-api-key` +- CLI: `openclaw onboard --auth-choice ai-gateway-api-key` ### Kilo Gateway - Provider: `kilocode` - Auth: `KILOCODE_API_KEY` - Example model: `kilocode/anthropic/claude-opus-4.6` -- CLI: `openclaw setup --wizard --kilocode-api-key ` +- CLI: `openclaw onboard --kilocode-api-key ` - Base URL: `https://api.kilo.ai/api/gateway/` - Expanded built-in catalog includes GLM-5 Free, MiniMax M2.5 Free, GPT-5.2, Gemini 3 Pro Preview, Gemini 3 Flash Preview, Grok Code Fast 1, and Kimi K2.5. @@ -262,13 +271,13 @@ See [/providers/kilocode](/providers/kilocode) for setup details. - xAI: `xai` (`XAI_API_KEY`) - Mistral: `mistral` (`MISTRAL_API_KEY`) - Example model: `mistral/mistral-large-latest` -- CLI: `openclaw setup --wizard --auth-choice mistral-api-key` +- CLI: `openclaw onboard --auth-choice mistral-api-key` - Groq: `groq` (`GROQ_API_KEY`) - Cerebras: `cerebras` (`CEREBRAS_API_KEY`) - GLM models on Cerebras use ids `zai-glm-4.7` and `zai-glm-4.6`. - OpenAI-compatible base URL: `https://api.cerebras.ai/v1`. - GitHub Copilot: `github-copilot` (`COPILOT_GITHUB_TOKEN` / `GH_TOKEN` / `GITHUB_TOKEN`) -- Hugging Face Inference example model: `huggingface/deepseek-ai/DeepSeek-R1`; CLI: `openclaw setup --wizard --auth-choice huggingface-api-key`. See [Hugging Face (Inference)](/providers/huggingface). +- Hugging Face Inference example model: `huggingface/deepseek-ai/DeepSeek-R1`; CLI: `openclaw onboard --auth-choice huggingface-api-key`. See [Hugging Face (Inference)](/providers/huggingface). ## Providers via `models.providers` (custom/base URL) @@ -358,7 +367,7 @@ Volcano Engine (火山引擎) provides access to Doubao and other models in Chin - Provider: `volcengine` (coding: `volcengine-plan`) - Auth: `VOLCANO_ENGINE_API_KEY` - Example model: `volcengine/doubao-seed-1-8-251228` -- CLI: `openclaw setup --wizard --auth-choice volcengine-api-key` +- CLI: `openclaw onboard --auth-choice volcengine-api-key` ```json5 { @@ -391,7 +400,7 @@ BytePlus ARK provides access to the same models as Volcano Engine for internatio - Provider: `byteplus` (coding: `byteplus-plan`) - Auth: `BYTEPLUS_API_KEY` - Example model: `byteplus/seed-1-8-251228` -- CLI: `openclaw setup --wizard --auth-choice byteplus-api-key` +- CLI: `openclaw onboard --auth-choice byteplus-api-key` ```json5 { @@ -422,7 +431,7 @@ Synthetic provides Anthropic-compatible models behind the `synthetic` provider: - Provider: `synthetic` - Auth: `SYNTHETIC_API_KEY` - Example model: `synthetic/hf:MiniMaxAI/MiniMax-M2.5` -- CLI: `openclaw setup --wizard --auth-choice synthetic-api-key` +- CLI: `openclaw onboard --auth-choice synthetic-api-key` ```json5 { @@ -476,7 +485,7 @@ ollama pull llama3.3 Ollama is detected locally at `http://127.0.0.1:11434` when you opt in with `OLLAMA_API_KEY`, and the bundled provider plugin adds Ollama directly to -`openclaw setup --wizard` and the model picker. See [/providers/ollama](/providers/ollama) +`openclaw onboard` and the model picker. See [/providers/ollama](/providers/ollama) for onboarding, cloud/local mode, and custom configuration. ### vLLM @@ -586,7 +595,7 @@ Notes: ## CLI examples ```bash -openclaw setup --wizard --auth-choice opencode-zen +openclaw onboard --auth-choice opencode-zen openclaw models set opencode/claude-opus-4-6 openclaw models list ``` diff --git a/docs/concepts/models.md b/docs/concepts/models.md index f190630ac36..e85e605456f 100644 --- a/docs/concepts/models.md +++ b/docs/concepts/models.md @@ -39,7 +39,7 @@ Related: If you don’t want to hand-edit config, run the setup wizard: ```bash -openclaw setup --wizard +openclaw onboard ``` It can set up model + auth for common providers, including **OpenAI Code (Codex) diff --git a/docs/concepts/oauth.md b/docs/concepts/oauth.md index 4b8b2739c22..4766687ad51 100644 --- a/docs/concepts/oauth.md +++ b/docs/concepts/oauth.md @@ -92,7 +92,7 @@ Flow shape: 2. paste the token into OpenClaw 3. store as a token auth profile (no refresh) -The wizard path is `openclaw setup --wizard` → auth choice `setup-token` (Anthropic). +The wizard path is `openclaw onboard` → auth choice `setup-token` (Anthropic). ### OpenAI Codex (ChatGPT OAuth) @@ -107,7 +107,7 @@ Flow shape (PKCE): 5. exchange at `https://auth.openai.com/oauth/token` 6. extract `accountId` from the access token and store `{ access, refresh, expires, accountId }` -Wizard path is `openclaw setup --wizard` → auth choice `openai-codex`. +Wizard path is `openclaw onboard` → auth choice `openai-codex`. ## Refresh + expiry diff --git a/docs/docs.json b/docs/docs.json index 229699ec37e..80409046397 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1018,7 +1018,6 @@ "pages": [ "tools/browser", "tools/browser-login", - "tools/chrome-extension", "tools/browser-linux-troubleshooting" ] }, @@ -1613,7 +1612,6 @@ "pages": [ "zh-CN/tools/browser", "zh-CN/tools/browser-login", - "zh-CN/tools/chrome-extension", "zh-CN/tools/browser-linux-troubleshooting" ] }, diff --git a/docs/gateway/authentication.md b/docs/gateway/authentication.md index fe8e5b760d3..c25501e6cdd 100644 --- a/docs/gateway/authentication.md +++ b/docs/gateway/authentication.md @@ -50,7 +50,7 @@ openclaw doctor ``` If you’d rather not manage env vars yourself, the setup wizard can store -API keys for daemon use: `openclaw setup --wizard`. +API keys for daemon use: `openclaw onboard`. See [Help](/help) for details on env inheritance (`env.shellEnv`, `~/.openclaw/.env`, systemd/launchd). diff --git a/docs/gateway/configuration-reference.md b/docs/gateway/configuration-reference.md index b28cde9c260..a46f342a360 100644 --- a/docs/gateway/configuration-reference.md +++ b/docs/gateway/configuration-reference.md @@ -2182,7 +2182,7 @@ Use `cerebras/zai-glm-4.7` for Cerebras; `zai/glm-4.7` for Z.AI direct. } ``` -Set `OPENCODE_API_KEY` (or `OPENCODE_ZEN_API_KEY`). Use `opencode/...` refs for the Zen catalog or `opencode-go/...` refs for the Go catalog. Shortcut: `openclaw setup --wizard --auth-choice opencode-zen` or `openclaw setup --wizard --auth-choice opencode-go`. +Set `OPENCODE_API_KEY` (or `OPENCODE_ZEN_API_KEY`). Use `opencode/...` refs for the Zen catalog or `opencode-go/...` refs for the Go catalog. Shortcut: `openclaw onboard --auth-choice opencode-zen` or `openclaw onboard --auth-choice opencode-go`. @@ -2199,7 +2199,7 @@ Set `OPENCODE_API_KEY` (or `OPENCODE_ZEN_API_KEY`). Use `opencode/...` refs for } ``` -Set `ZAI_API_KEY`. `z.ai/*` and `z-ai/*` are accepted aliases. Shortcut: `openclaw setup --wizard --auth-choice zai-api-key`. +Set `ZAI_API_KEY`. `z.ai/*` and `z-ai/*` are accepted aliases. Shortcut: `openclaw onboard --auth-choice zai-api-key`. - General endpoint: `https://api.z.ai/api/paas/v4` - Coding endpoint (default): `https://api.z.ai/api/coding/paas/v4` @@ -2242,7 +2242,7 @@ Set `ZAI_API_KEY`. `z.ai/*` and `z-ai/*` are accepted aliases. Shortcut: `opencl } ``` -For the China endpoint: `baseUrl: "https://api.moonshot.cn/v1"` or `openclaw setup --wizard --auth-choice moonshot-api-key-cn`. +For the China endpoint: `baseUrl: "https://api.moonshot.cn/v1"` or `openclaw onboard --auth-choice moonshot-api-key-cn`. @@ -2260,7 +2260,7 @@ For the China endpoint: `baseUrl: "https://api.moonshot.cn/v1"` or `openclaw set } ``` -Anthropic-compatible, built-in provider. Shortcut: `openclaw setup --wizard --auth-choice kimi-code-api-key`. +Anthropic-compatible, built-in provider. Shortcut: `openclaw onboard --auth-choice kimi-code-api-key`. @@ -2299,7 +2299,7 @@ Anthropic-compatible, built-in provider. Shortcut: `openclaw setup --wizard --au } ``` -Base URL should omit `/v1` (Anthropic client appends it). Shortcut: `openclaw setup --wizard --auth-choice synthetic-api-key`. +Base URL should omit `/v1` (Anthropic client appends it). Shortcut: `openclaw onboard --auth-choice synthetic-api-key`. @@ -2339,7 +2339,7 @@ Base URL should omit `/v1` (Anthropic client appends it). Shortcut: `openclaw se } ``` -Set `MINIMAX_API_KEY`. Shortcut: `openclaw setup --wizard --auth-choice minimax-api`. +Set `MINIMAX_API_KEY`. Shortcut: `openclaw onboard --auth-choice minimax-api`. @@ -2442,13 +2442,13 @@ See [Plugins](/tools/plugin). profiles: { openclaw: { cdpPort: 18800, color: "#FF4500" }, work: { cdpPort: 18801, color: "#0066CC" }, + user: { driver: "existing-session", attachOnly: true, color: "#00AA00" }, remote: { cdpUrl: "http://10.0.0.42:9222", color: "#00AA00" }, }, color: "#FF4500", // headless: false, // noSandbox: false, // extraArgs: [], - // relayBindHost: "0.0.0.0", // only when the extension relay must be reachable across namespaces (for example WSL2) // executablePath: "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser", // attachOnly: false, }, @@ -2462,11 +2462,11 @@ See [Plugins](/tools/plugin). - `ssrfPolicy.allowPrivateNetwork` remains supported as a legacy alias. - In strict mode, use `ssrfPolicy.hostnameAllowlist` and `ssrfPolicy.allowedHostnames` for explicit exceptions. - Remote profiles are attach-only (start/stop/reset disabled). +- `existing-session` profiles are host-only and use Chrome MCP instead of CDP. - Auto-detect order: default browser if Chromium-based → Chrome → Brave → Edge → Chromium → Chrome Canary. - Control service: loopback only (port derived from `gateway.port`, default `18791`). - `extraArgs` appends extra launch flags to local Chromium startup (for example `--disable-gpu`, window sizing, or debug flags). -- `relayBindHost` changes where the Chrome extension relay listens. Leave unset for loopback-only access; set an explicit non-loopback bind address such as `0.0.0.0` only when the relay must cross a namespace boundary (for example WSL2) and the host network is already trusted. --- diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index db4bb167417..a699e74652f 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -20,7 +20,7 @@ If the file is missing, OpenClaw uses safe defaults. Common reasons to add a con See the [full reference](/gateway/configuration-reference) for every available field. -**New to configuration?** Start with `openclaw setup --wizard` for interactive setup, or check out the [Configuration Examples](/gateway/configuration-examples) guide for complete copy-paste configs. +**New to configuration?** Start with `openclaw onboard` for interactive setup, or check out the [Configuration Examples](/gateway/configuration-examples) guide for complete copy-paste configs. ## Minimal config @@ -38,7 +38,7 @@ See the [full reference](/gateway/configuration-reference) for every available f ```bash - openclaw setup --wizard # full setup wizard + openclaw onboard # full setup wizard openclaw configure # config wizard ``` diff --git a/docs/gateway/doctor.md b/docs/gateway/doctor.md index 95027906750..6c0711c7aea 100644 --- a/docs/gateway/doctor.md +++ b/docs/gateway/doctor.md @@ -63,6 +63,7 @@ cat ~/.openclaw/openclaw.json - Health check + restart prompt. - Skills status summary (eligible/missing/blocked). - Config normalization for legacy values. +- Browser migration checks for legacy Chrome extension configs and Chrome MCP readiness. - OpenCode provider override warnings (`models.providers.opencode` / `models.providers.opencode-go`). - Legacy on-disk state migration (sessions/agent dir/WhatsApp auth). - Legacy cron store migration (`jobId`, `schedule.cron`, top-level delivery/payload fields, payload `provider`, simple `notify: true` webhook fallback jobs). @@ -128,6 +129,8 @@ Current migrations: - `agent.model`/`allowedModels`/`modelAliases`/`modelFallbacks`/`imageModelFallbacks` → `agents.defaults.models` + `agents.defaults.model.primary/fallbacks` + `agents.defaults.imageModel.primary/fallbacks` - `browser.ssrfPolicy.allowPrivateNetwork` → `browser.ssrfPolicy.dangerouslyAllowPrivateNetwork` +- `browser.profiles.*.driver: "extension"` → `"existing-session"` +- remove `browser.relayBindHost` (legacy extension relay setting) Doctor warnings also include account-default guidance for multi-account channels: @@ -141,6 +144,33 @@ manually, it overrides the built-in OpenCode catalog from `@mariozechner/pi-ai`. That can force models onto the wrong API or zero out costs. Doctor warns so you can remove the override and restore per-model API routing + costs. +### 2c) Browser migration and Chrome MCP readiness + +If your browser config still points at the removed Chrome extension path, doctor +normalizes it to the current host-local Chrome MCP attach model: + +- `browser.profiles.*.driver: "extension"` becomes `"existing-session"` +- `browser.relayBindHost` is removed + +Doctor also audits the host-local Chrome MCP path when you use `defaultProfile: +"user"` or a configured `existing-session` profile: + +- checks whether Google Chrome is installed on the same host +- checks the detected Chrome version and warns when it is below Chrome 144 +- reminds you to enable remote debugging in Chrome at + `chrome://inspect/#remote-debugging` + +Doctor cannot enable the Chrome-side setting for you. Host-local Chrome MCP +still requires: + +- Google Chrome 144+ on the gateway/node host +- Chrome running locally +- remote debugging enabled in Chrome +- approving the first attach consent prompt in Chrome + +This check does **not** apply to Docker, sandbox, remote-browser, or other +headless flows. Those continue to use raw CDP. + ### 3) Legacy state migrations (disk layout) Doctor can migrate older on-disk layouts into the current structure: diff --git a/docs/gateway/local-models.md b/docs/gateway/local-models.md index 93a63c38170..4059f988776 100644 --- a/docs/gateway/local-models.md +++ b/docs/gateway/local-models.md @@ -11,7 +11,7 @@ title: "Local Models" Local is doable, but OpenClaw expects large context + strong defenses against prompt injection. Small cards truncate context and leak safety. Aim high: **≥2 maxed-out Mac Studios or equivalent GPU rig (~$30k+)**. A single **24 GB** GPU works only for lighter prompts with higher latency. Use the **largest / full-size model variant you can run**; aggressively quantized or “small” checkpoints raise prompt-injection risk (see [Security](/gateway/security)). -If you want the lowest-friction local setup, start with [Ollama](/providers/ollama) and `openclaw setup --wizard`. This page is the opinionated guide for higher-end local stacks and custom OpenAI-compatible local servers. +If you want the lowest-friction local setup, start with [Ollama](/providers/ollama) and `openclaw onboard`. This page is the opinionated guide for higher-end local stacks and custom OpenAI-compatible local servers. ## Recommended: LM Studio + MiniMax M2.5 (Responses API, full-size) diff --git a/docs/gateway/multiple-gateways.md b/docs/gateway/multiple-gateways.md index b2d5257f0ff..6d1cf423b98 100644 --- a/docs/gateway/multiple-gateways.md +++ b/docs/gateway/multiple-gateways.md @@ -59,7 +59,7 @@ Port spacing: leave at least 20 ports between base ports so the derived browser/ ```bash # Main bot (existing or fresh, without --profile param) # Runs on port 18789 + Chrome CDC/Canvas/... Ports -openclaw setup --wizard +openclaw onboard openclaw gateway install # Rescue bot (isolated profile + ports) diff --git a/docs/gateway/security/index.md b/docs/gateway/security/index.md index 595e50f2628..68be08fbed5 100644 --- a/docs/gateway/security/index.md +++ b/docs/gateway/security/index.md @@ -990,10 +990,9 @@ access those accounts and data. Treat browser profiles as **sensitive state**: - Treat browser downloads as untrusted input; prefer an isolated downloads directory. - Disable browser sync/password managers in the agent profile if possible (reduces blast radius). - For remote gateways, assume “browser control” is equivalent to “operator access” to whatever that profile can reach. -- Keep the Gateway and node hosts tailnet-only; avoid exposing relay/control ports to LAN or public Internet. -- The Chrome extension relay’s CDP endpoint is auth-gated; only OpenClaw clients can connect. +- Keep the Gateway and node hosts tailnet-only; avoid exposing browser control ports to LAN or public Internet. - Disable browser proxy routing when you don’t need it (`gateway.nodes.browser.mode="off"`). -- Chrome extension relay mode is **not** “safer”; it can take over your existing Chrome tabs. Assume it can act as you in whatever that tab/profile can reach. +- Chrome MCP existing-session mode is **not** “safer”; it can act as you in whatever that host Chrome profile can reach. ### Browser SSRF policy (trusted-network default) diff --git a/docs/gateway/troubleshooting.md b/docs/gateway/troubleshooting.md index 41c697a67f1..aa75b9cf2b5 100644 --- a/docs/gateway/troubleshooting.md +++ b/docs/gateway/troubleshooting.md @@ -289,19 +289,18 @@ Look for: - Valid browser executable path. - CDP profile reachability. -- Extension relay tab attachment (if an extension relay profile is configured). +- Local Chrome availability for `existing-session` / `user` profiles. Common signatures: - `Failed to start Chrome CDP on port` → browser process failed to launch. - `browser.executablePath not found` → configured path is invalid. -- `Chrome extension relay is running, but no tab is connected` → extension relay not attached. +- `No Chrome tabs found for profile="user"` → the Chrome MCP attach profile has no open local Chrome tabs. - `Browser attachOnly is enabled ... not reachable` → attach-only profile has no reachable target. Related: - [/tools/browser-linux-troubleshooting](/tools/browser-linux-troubleshooting) -- [/tools/chrome-extension](/tools/chrome-extension) - [/tools/browser](/tools/browser) ## If you upgraded and something suddenly broke diff --git a/docs/help/faq.md b/docs/help/faq.md index 670ea170c19..b32b1aac8c5 100644 --- a/docs/help/faq.md +++ b/docs/help/faq.md @@ -80,7 +80,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS, - [Can OpenClaw run tasks on a schedule or continuously in the background?](#can-openclaw-run-tasks-on-a-schedule-or-continuously-in-the-background) - [Can I run Apple macOS-only skills from Linux?](#can-i-run-apple-macos-only-skills-from-linux) - [Do you have a Notion or HeyGen integration?](#do-you-have-a-notion-or-heygen-integration) - - [How do I install the Chrome extension for browser takeover?](#how-do-i-install-the-chrome-extension-for-browser-takeover) + - [How do I use my existing signed-in Chrome with OpenClaw?](#how-do-i-use-my-existing-signed-in-chrome-with-openclaw) - [Sandboxing and memory](#sandboxing-and-memory) - [Is there a dedicated sandboxing doc?](#is-there-a-dedicated-sandboxing-doc) - [How do I bind a host folder into the sandbox?](#how-do-i-bind-a-host-folder-into-the-sandbox) @@ -321,7 +321,7 @@ The repo recommends running from source and using the setup wizard: ```bash curl -fsSL https://openclaw.ai/install.sh | bash -openclaw setup --wizard --install-daemon +openclaw onboard --install-daemon ``` The wizard can also build UI assets automatically. After onboarding, you typically run the Gateway on port **18789**. @@ -334,10 +334,10 @@ cd openclaw pnpm install pnpm build pnpm ui:build # auto-installs UI deps on first run -openclaw setup --wizard +openclaw onboard ``` -If you don't have a global install yet, run it via `pnpm openclaw setup --wizard`. +If you don't have a global install yet, run it via `pnpm openclaw onboard`. ### How do I open the dashboard after onboarding @@ -687,7 +687,7 @@ Docs: [Update](/cli/update), [Updating](/install/updating). ### What does the setup wizard actually do -`openclaw setup --wizard` is the recommended setup path. In **local mode** it walks you through: +`openclaw onboard` is the recommended setup path. In **local mode** it walks you through: - **Model/auth setup** (provider OAuth/setup-token flows and API keys supported, plus local model options such as LM Studio) - **Workspace** location + bootstrap files @@ -1214,22 +1214,23 @@ clawhub update --all ClawHub installs into `./skills` under your current directory (or falls back to your configured OpenClaw workspace); OpenClaw treats that as `/skills` on the next session. For shared skills across agents, place them in `~/.openclaw/skills//SKILL.md`. Some skills expect binaries installed via Homebrew; on Linux that means Linuxbrew (see the Homebrew Linux FAQ entry above). See [Skills](/tools/skills) and [ClawHub](/tools/clawhub). -### How do I install the Chrome extension for browser takeover +### How do I use my existing signed-in Chrome with OpenClaw -Use the built-in installer, then load the unpacked extension in Chrome: +Use the built-in `user` browser profile, which attaches through Chrome DevTools MCP: ```bash -openclaw browser extension install -openclaw browser extension path +openclaw browser --browser-profile user tabs +openclaw browser --browser-profile user snapshot ``` -Then Chrome → `chrome://extensions` → enable "Developer mode" → "Load unpacked" → pick that folder. +If you want a custom name, create an explicit MCP profile: -Full guide (including remote Gateway + security notes): [Chrome extension](/tools/chrome-extension) +```bash +openclaw browser create-profile --name chrome-live --driver existing-session +openclaw browser --browser-profile chrome-live tabs +``` -If the Gateway runs on the same machine as Chrome (default setup), you usually **do not** need anything extra. -If the Gateway runs elsewhere, run a node host on the browser machine so the Gateway can proxy browser actions. -You still need to click the extension button on the tab you want to control (it doesn't auto-attach). +This path is host-local. If the Gateway runs elsewhere, either run a node host on the browser machine or use remote CDP instead. ## Sandboxing and memory @@ -1665,13 +1666,12 @@ setup is an always-on host plus your laptop as a node. - **No inbound SSH required.** Nodes connect out to the Gateway WebSocket and use device pairing. - **Safer execution controls.** `system.run` is gated by node allowlists/approvals on that laptop. - **More device tools.** Nodes expose `canvas`, `camera`, and `screen` in addition to `system.run`. -- **Local browser automation.** Keep the Gateway on a VPS, but run Chrome locally and relay control - with the Chrome extension + a node host on the laptop. +- **Local browser automation.** Keep the Gateway on a VPS, but run Chrome locally through a node host on the laptop, or attach to local Chrome on the host via Chrome MCP. SSH is fine for ad-hoc shell access, but nodes are simpler for ongoing agent workflows and device automation. -Docs: [Nodes](/nodes), [Nodes CLI](/cli/nodes), [Chrome extension](/tools/chrome-extension). +Docs: [Nodes](/nodes), [Nodes CLI](/cli/nodes), [Browser](/tools/browser). ### Should I install on a second laptop or just add a node @@ -1904,7 +1904,7 @@ openclaw reset --scope full --yes --non-interactive Then re-run setup: ```bash -openclaw setup --wizard --install-daemon +openclaw onboard --install-daemon ``` Notes: @@ -2039,18 +2039,18 @@ Yes. Use **Multi-Agent Routing** to run multiple isolated agents and route inbou channel/account/peer. Slack is supported as a channel and can be bound to specific agents. Browser access is powerful but not "do anything a human can" - anti-bot, CAPTCHAs, and MFA can -still block automation. For the most reliable browser control, use the Chrome extension relay -on the machine that runs the browser (and keep the Gateway anywhere). +still block automation. For the most reliable browser control, use local Chrome MCP on the host, +or use CDP on the machine that actually runs the browser. Best-practice setup: - Always-on Gateway host (VPS/Mac mini). - One agent per role (bindings). - Slack channel(s) bound to those agents. -- Local browser via extension relay (or a node) when needed. +- Local browser via Chrome MCP or a node when needed. Docs: [Multi-Agent Routing](/concepts/multi-agent), [Slack](/channels/slack), -[Browser](/tools/browser), [Chrome extension](/tools/chrome-extension), [Nodes](/nodes). +[Browser](/tools/browser), [Nodes](/nodes). ## Models: defaults, selection, aliases, switching @@ -2092,7 +2092,7 @@ Quickest setup: 1. Install Ollama from `https://ollama.com/download` 2. Pull a local model such as `ollama pull glm-4.7-flash` 3. If you want Ollama Cloud too, run `ollama signin` -4. Run `openclaw setup --wizard` and choose `Ollama` +4. Run `openclaw onboard` and choose `Ollama` 5. Pick `Local` or `Cloud + Local` Notes: diff --git a/docs/help/testing.md b/docs/help/testing.md index b2057e8a1da..09388dd769e 100644 --- a/docs/help/testing.md +++ b/docs/help/testing.md @@ -61,7 +61,7 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost): - Command: `pnpm test:e2e` - Config: `vitest.e2e.config.ts` -- Files: `src/**/*.e2e.test.ts` +- Files: `src/**/*.e2e.test.ts`, `test/**/*.e2e.test.ts` - Runtime defaults: - Uses Vitest `vmForks` for faster file startup. - Uses adaptive workers (CI: 2-4, local: 4-8). @@ -77,6 +77,23 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost): - No real keys required - More moving parts than unit tests (can be slower) +### E2E: OpenShell backend smoke + +- Command: `pnpm test:e2e:openshell` +- File: `test/openshell-sandbox.e2e.test.ts` +- Scope: + - Starts an isolated OpenShell gateway on the host via Docker + - Creates a sandbox from a temporary local Dockerfile + - Exercises OpenClaw's OpenShell backend over real `sandbox ssh-config` + SSH exec + - Verifies remote-canonical filesystem behavior through the sandbox fs bridge +- Expectations: + - Opt-in only; not part of the default `pnpm test:e2e` run + - Requires a local `openshell` CLI plus a working Docker daemon + - Uses isolated `HOME` / `XDG_CONFIG_HOME`, then destroys the test gateway and sandbox +- Useful overrides: + - `OPENCLAW_E2E_OPENSHELL=1` to enable the test when running the broader e2e suite manually + - `OPENCLAW_E2E_OPENSHELL_COMMAND=/path/to/openshell` to point at a non-default CLI binary or wrapper script + ### Live (real providers + real models) - Command: `pnpm test:live` @@ -345,7 +362,7 @@ If you want to rely on env keys (e.g. exported in your `~/.profile`), run local ## Docker runners (optional “works in Linux” checks) -These run `pnpm test:live` inside the repo Docker image, mounting your local config dir and workspace (and sourcing `~/.profile` if mounted): +These run `pnpm test:live` inside the repo Docker image, mounting your local config dir and workspace (and sourcing `~/.profile` if mounted). They also bind-mount CLI auth homes like `~/.codex`, `~/.claude`, `~/.qwen`, and `~/.minimax` when present so external-CLI OAuth stays available in-container: - Direct models: `pnpm test:docker:live-models` (script: `scripts/test-live-models-docker.sh`) - Gateway + dev agent: `pnpm test:docker:live-gateway` (script: `scripts/test-live-gateway-models-docker.sh`) @@ -367,6 +384,7 @@ Useful env vars: - `OPENCLAW_CONFIG_DIR=...` (default: `~/.openclaw`) mounted to `/home/node/.openclaw` - `OPENCLAW_WORKSPACE_DIR=...` (default: `~/.openclaw/workspace`) mounted to `/home/node/.openclaw/workspace` - `OPENCLAW_PROFILE_FILE=...` (default: `~/.profile`) mounted to `/home/node/.profile` and sourced before running tests +- External CLI auth dirs under `$HOME` (`.codex`, `.claude`, `.qwen`, `.minimax`) are mounted read-only to the matching `/home/node/...` paths when present - `OPENCLAW_LIVE_GATEWAY_MODELS=...` / `OPENCLAW_LIVE_MODELS=...` to narrow the run - `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` to ensure creds come from the profile store (not env) diff --git a/docs/help/troubleshooting.md b/docs/help/troubleshooting.md index a3988c4ea58..1660100ba8c 100644 --- a/docs/help/troubleshooting.md +++ b/docs/help/troubleshooting.md @@ -278,13 +278,13 @@ flowchart TD Good output looks like: - Browser status shows `running: true` and a chosen browser/profile. - - `openclaw` profile starts or `chrome` relay has an attached tab. + - `openclaw` starts, or `user` can see local Chrome tabs. Common log signatures: - `Failed to start Chrome CDP on port` → local browser launch failed. - `browser.executablePath not found` → configured binary path is wrong. - - `Chrome extension relay is running, but no tab is connected` → extension not attached. + - `No Chrome tabs found for profile="user"` → the Chrome MCP attach profile has no open local Chrome tabs. - `Browser attachOnly is enabled ... not reachable` → attach-only profile has no live CDP target. Deep pages: @@ -292,7 +292,6 @@ flowchart TD - [/gateway/troubleshooting#browser-tool-fails](/gateway/troubleshooting#browser-tool-fails) - [/tools/browser-linux-troubleshooting](/tools/browser-linux-troubleshooting) - [/tools/browser-wsl2-windows-remote-cdp-troubleshooting](/tools/browser-wsl2-windows-remote-cdp-troubleshooting) - - [/tools/chrome-extension](/tools/chrome-extension) diff --git a/docs/index.md b/docs/index.md index e8c2210caff..7c69600f55d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -34,7 +34,7 @@ title: "OpenClaw" Install OpenClaw and bring up the Gateway in minutes. - Guided setup with `openclaw setup --wizard` and pairing flows. + Guided setup with `openclaw onboard` and pairing flows. Launch the browser dashboard for chat, config, and sessions. @@ -103,7 +103,7 @@ The Gateway is the single source of truth for sessions, routing, and channel con ```bash - openclaw setup --wizard --install-daemon + openclaw onboard --install-daemon ``` diff --git a/docs/install/docker.md b/docs/install/docker.md index a3827075202..a9f6b578bd0 100644 --- a/docs/install/docker.md +++ b/docs/install/docker.md @@ -713,6 +713,7 @@ an optional noVNC observer (headful via Xvfb). Notes: +- Docker and other headless/container browser flows stay on raw CDP. Chrome MCP `existing-session` is for host-local Chrome, not container takeover. - Headful (Xvfb) reduces bot blocking vs headless. - Headless can still be used by setting `agents.defaults.sandbox.browser.headless=true`. - No full desktop environment (GNOME) is needed; Xvfb provides the display. diff --git a/docs/install/exe-dev.md b/docs/install/exe-dev.md index b66865593da..c49dab4e426 100644 --- a/docs/install/exe-dev.md +++ b/docs/install/exe-dev.md @@ -31,7 +31,7 @@ Shelley, [exe.dev](https://exe.dev)'s agent, can install OpenClaw instantly with prompt. The prompt used is as below: ``` -Set up OpenClaw (https://docs.openclaw.ai/install) on this VM. Use the non-interactive and accept-risk flags for openclaw setup --wizarding. Add the supplied auth or token as needed. Configure nginx to forward from the default port 18789 to the root location on the default enabled site config, making sure to enable Websocket support. Pairing is done by "openclaw devices list" and "openclaw devices approve ". Make sure the dashboard shows that OpenClaw's health is OK. exe.dev handles forwarding from port 8000 to port 80/443 and HTTPS for us, so the final "reachable" should be .exe.xyz, without port specification. +Set up OpenClaw (https://docs.openclaw.ai/install) on this VM. Use the non-interactive and accept-risk flags for openclaw onboarding. Add the supplied auth or token as needed. Configure nginx to forward from the default port 18789 to the root location on the default enabled site config, making sure to enable Websocket support. Pairing is done by "openclaw devices list" and "openclaw devices approve ". Make sure the dashboard shows that OpenClaw's health is OK. exe.dev handles forwarding from port 8000 to port 80/443 and HTTPS for us, so the final "reachable" should be .exe.xyz, without port specification. ``` ## Manual installation diff --git a/docs/install/index.md b/docs/install/index.md index 59396c49b5f..21adfdaa592 100644 --- a/docs/install/index.md +++ b/docs/install/index.md @@ -76,7 +76,7 @@ For VPS/cloud hosts, avoid third-party "1-click" marketplace images when possibl ```bash npm install -g openclaw@latest - openclaw setup --wizard --install-daemon + openclaw onboard --install-daemon ``` @@ -93,7 +93,7 @@ For VPS/cloud hosts, avoid third-party "1-click" marketplace images when possibl ```bash pnpm add -g openclaw@latest pnpm approve-builds -g # approve openclaw, node-llama-cpp, sharp, etc. - openclaw setup --wizard --install-daemon + openclaw onboard --install-daemon ``` @@ -140,7 +140,7 @@ For VPS/cloud hosts, avoid third-party "1-click" marketplace images when possibl ```bash - openclaw setup --wizard --install-daemon + openclaw onboard --install-daemon ``` diff --git a/docs/install/installer.md b/docs/install/installer.md index 813fa7b31b4..5859c22fd0d 100644 --- a/docs/install/installer.md +++ b/docs/install/installer.md @@ -224,7 +224,7 @@ Designed for environments where you want everything under a local prefix (defaul | `--version ` | OpenClaw version or dist-tag (default: `latest`) | | `--node-version ` | Node version (default: `22.22.0`) | | `--json` | Emit NDJSON events | -| `--onboard` | Run `openclaw setup --wizard` after install | +| `--onboard` | Run `openclaw onboard` after install | | `--no-onboard` | Skip onboarding (default) | | `--set-npm-prefix` | On Linux, force npm prefix to `~/.npm-global` if current prefix is not writable | | `--help` | Show usage (`-h`) | diff --git a/docs/install/macos-vm.md b/docs/install/macos-vm.md index 3e036c6ee0d..f2eadfda113 100644 --- a/docs/install/macos-vm.md +++ b/docs/install/macos-vm.md @@ -138,7 +138,7 @@ Inside the VM: ```bash npm install -g openclaw@latest -openclaw setup --wizard --install-daemon +openclaw onboard --install-daemon ``` Follow the onboarding prompts to set up your model provider (Anthropic, OpenAI, etc.). diff --git a/docs/platforms/digitalocean.md b/docs/platforms/digitalocean.md index aaea2644ca6..cd05587ae76 100644 --- a/docs/platforms/digitalocean.md +++ b/docs/platforms/digitalocean.md @@ -80,7 +80,7 @@ openclaw --version ## 4) Run Onboarding ```bash -openclaw setup --wizard --install-daemon +openclaw onboard --install-daemon ``` The wizard will walk you through: diff --git a/docs/platforms/index.md b/docs/platforms/index.md index 3c7ecca0f48..ec2663aefe4 100644 --- a/docs/platforms/index.md +++ b/docs/platforms/index.md @@ -42,7 +42,7 @@ Native companion apps for Windows are also planned; the Gateway is recommended v Use one of these (all supported): -- Wizard (recommended): `openclaw setup --wizard --install-daemon` +- Wizard (recommended): `openclaw onboard --install-daemon` - Direct: `openclaw gateway install` - Configure flow: `openclaw configure` → select **Gateway service** - Repair/migrate: `openclaw doctor` (offers to install or fix the service) diff --git a/docs/platforms/linux.md b/docs/platforms/linux.md index 29de3dd47ea..c03dba6f795 100644 --- a/docs/platforms/linux.md +++ b/docs/platforms/linux.md @@ -17,7 +17,7 @@ Native Linux companion apps are planned. Contributions are welcome if you want t 1. Install Node 24 (recommended; Node 22 LTS, currently `22.16+`, still works for compatibility) 2. `npm i -g openclaw@latest` -3. `openclaw setup --wizard --install-daemon` +3. `openclaw onboard --install-daemon` 4. From your laptop: `ssh -N -L 18789:127.0.0.1:18789 @` 5. Open `http://127.0.0.1:18789/` and paste your token @@ -39,7 +39,7 @@ Step-by-step VPS guide: [exe.dev](/install/exe-dev) Use one of these: ``` -openclaw setup --wizard --install-daemon +openclaw onboard --install-daemon ``` Or: diff --git a/docs/platforms/raspberry-pi.md b/docs/platforms/raspberry-pi.md index 4a3bf7b8204..2050b6395b4 100644 --- a/docs/platforms/raspberry-pi.md +++ b/docs/platforms/raspberry-pi.md @@ -130,7 +130,7 @@ The hackable install gives you direct access to logs and code — useful for deb ## 7) Run Onboarding ```bash -openclaw setup --wizard --install-daemon +openclaw onboard --install-daemon ``` Follow the wizard: diff --git a/docs/platforms/windows.md b/docs/platforms/windows.md index c8047271e65..e40d798604d 100644 --- a/docs/platforms/windows.md +++ b/docs/platforms/windows.md @@ -38,8 +38,8 @@ openclaw agent --local --agent main --thinking low -m "Reply with exactly WINDOW Current caveats: -- `openclaw setup --wizard --non-interactive` still expects a reachable local gateway unless you pass `--skip-health` -- `openclaw setup --wizard --non-interactive --install-daemon` and `openclaw gateway install` try Windows Scheduled Tasks first +- `openclaw onboard --non-interactive` still expects a reachable local gateway unless you pass `--skip-health` +- `openclaw onboard --non-interactive --install-daemon` and `openclaw gateway install` try Windows Scheduled Tasks first - if Scheduled Task creation is denied, OpenClaw falls back to a per-user Startup-folder login item and starts the gateway immediately - if `schtasks` itself wedges or stops responding, OpenClaw now aborts that path quickly and falls back instead of hanging forever - Scheduled Tasks are still preferred when available because they provide better supervisor status @@ -47,7 +47,7 @@ Current caveats: If you want the native CLI only, without gateway service install, use one of these: ```powershell -openclaw setup --wizard --non-interactive --skip-health +openclaw onboard --non-interactive --skip-health openclaw gateway run ``` @@ -70,7 +70,7 @@ If Scheduled Task creation is blocked, the fallback service mode still auto-star Inside WSL2: ``` -openclaw setup --wizard --install-daemon +openclaw onboard --install-daemon ``` Or: @@ -230,7 +230,7 @@ cd openclaw pnpm install pnpm ui:build # auto-installs UI deps on first run pnpm build -openclaw setup --wizard +openclaw onboard ``` Full guide: [Getting Started](/start/getting-started) diff --git a/docs/plugins/manifest.md b/docs/plugins/manifest.md index 01d5e0d3578..5ef77b9ef68 100644 --- a/docs/plugins/manifest.md +++ b/docs/plugins/manifest.md @@ -59,12 +59,49 @@ Optional keys: - `providerAuthEnvVars` (object): auth env vars keyed by provider id. Use this when OpenClaw should resolve provider credentials from env without loading plugin runtime first. +- `providerAuthChoices` (array): cheap onboarding/auth-choice metadata keyed by + provider + auth method. Use this when OpenClaw should show a provider in + auth-choice pickers, preferred-provider resolution, and CLI help without + loading plugin runtime first. - `skills` (array): skill directories to load (relative to the plugin root). - `name` (string): display name for the plugin. - `description` (string): short plugin summary. - `uiHints` (object): config field labels/placeholders/sensitive flags for UI rendering. - `version` (string): plugin version (informational). +### `providerAuthChoices` shape + +Each entry can declare: + +- `provider`: provider id +- `method`: auth method id +- `choiceId`: stable onboarding/auth-choice id +- `choiceLabel` / `choiceHint`: picker label + short hint +- `groupId` / `groupLabel` / `groupHint`: grouped onboarding bucket metadata +- `optionKey` / `cliFlag` / `cliOption` / `cliDescription`: optional one-flag + CLI wiring for simple auth flows such as API keys + +Example: + +```json +{ + "providerAuthChoices": [ + { + "provider": "openrouter", + "method": "api-key", + "choiceId": "openrouter-api-key", + "choiceLabel": "OpenRouter API key", + "groupId": "openrouter", + "groupLabel": "OpenRouter", + "optionKey": "openrouterApiKey", + "cliFlag": "--openrouter-api-key", + "cliOption": "--openrouter-api-key ", + "cliDescription": "OpenRouter API key" + } + ] +} +``` + ## JSON Schema requirements - **Every plugin must ship a JSON Schema**, even if it accepts no config. @@ -90,6 +127,9 @@ Optional keys: - `providerAuthEnvVars` is the cheap metadata path for auth probes, env-marker validation, and similar provider-auth surfaces that should not boot plugin runtime just to inspect env names. +- `providerAuthChoices` is the cheap metadata path for auth-choice pickers, + `--auth-choice` resolution, preferred-provider mapping, and simple onboarding + CLI flag registration before provider runtime loads. - Exclusive plugin kinds are selected through `plugins.slots.*`. - `kind: "memory"` is selected by `plugins.slots.memory`. - `kind: "context-engine"` is selected by `plugins.slots.contextEngine` diff --git a/docs/providers/anthropic.md b/docs/providers/anthropic.md index 5611eec7ba4..d16d76f6315 100644 --- a/docs/providers/anthropic.md +++ b/docs/providers/anthropic.md @@ -19,11 +19,11 @@ Create your API key in the Anthropic Console. ### CLI setup ```bash -openclaw setup --wizard +openclaw onboard # choose: Anthropic API key # or non-interactive -openclaw setup --wizard --anthropic-api-key "$ANTHROPIC_API_KEY" +openclaw onboard --anthropic-api-key "$ANTHROPIC_API_KEY" ``` ### Config snippet @@ -214,7 +214,7 @@ openclaw models auth paste-token --provider anthropic ```bash # Paste a setup-token during setup -openclaw setup --wizard --auth-choice setup-token +openclaw onboard --auth-choice setup-token ``` ### Config snippet (setup-token) diff --git a/docs/providers/cloudflare-ai-gateway.md b/docs/providers/cloudflare-ai-gateway.md index 63f471413e8..392a611e705 100644 --- a/docs/providers/cloudflare-ai-gateway.md +++ b/docs/providers/cloudflare-ai-gateway.md @@ -22,7 +22,7 @@ For Anthropic models, use your Anthropic API key. 1. Set the provider API key and Gateway details: ```bash -openclaw setup --wizard --auth-choice cloudflare-ai-gateway-api-key +openclaw onboard --auth-choice cloudflare-ai-gateway-api-key ``` 2. Set a default model: @@ -40,7 +40,7 @@ openclaw setup --wizard --auth-choice cloudflare-ai-gateway-api-key ## Non-interactive example ```bash -openclaw setup --wizard --non-interactive \ +openclaw onboard --non-interactive \ --mode local \ --auth-choice cloudflare-ai-gateway-api-key \ --cloudflare-ai-gateway-account-id "your-account-id" \ diff --git a/docs/providers/glm.md b/docs/providers/glm.md index bd096212cd0..64fe39a42df 100644 --- a/docs/providers/glm.md +++ b/docs/providers/glm.md @@ -15,16 +15,16 @@ models are accessed via the `zai` provider and model IDs like `zai/glm-5`. ```bash # Coding Plan Global, recommended for Coding Plan users -openclaw setup --wizard --auth-choice zai-coding-global +openclaw onboard --auth-choice zai-coding-global # Coding Plan CN (China region), recommended for Coding Plan users -openclaw setup --wizard --auth-choice zai-coding-cn +openclaw onboard --auth-choice zai-coding-cn # General API -openclaw setup --wizard --auth-choice zai-global +openclaw onboard --auth-choice zai-global # General API CN (China region) -openclaw setup --wizard --auth-choice zai-cn +openclaw onboard --auth-choice zai-cn ``` ## Config snippet diff --git a/docs/providers/huggingface.md b/docs/providers/huggingface.md index 416037dca49..7b33955f524 100644 --- a/docs/providers/huggingface.md +++ b/docs/providers/huggingface.md @@ -21,7 +21,7 @@ title: "Hugging Face (Inference)" 2. Run onboarding and choose **Hugging Face** in the provider dropdown, then enter your API key when prompted: ```bash -openclaw setup --wizard --auth-choice huggingface-api-key +openclaw onboard --auth-choice huggingface-api-key ``` 3. In the **Default Hugging Face model** dropdown, pick the model you want (the list is loaded from the Inference API when you have a valid token; otherwise a built-in list is shown). Your choice is saved as the default model. @@ -40,7 +40,7 @@ openclaw setup --wizard --auth-choice huggingface-api-key ## Non-interactive example ```bash -openclaw setup --wizard --non-interactive \ +openclaw onboard --non-interactive \ --mode local \ --auth-choice huggingface-api-key \ --huggingface-api-key "$HF_TOKEN" diff --git a/docs/providers/index.md b/docs/providers/index.md index 0e5c181f56b..f68cd0e0b53 100644 --- a/docs/providers/index.md +++ b/docs/providers/index.md @@ -15,7 +15,7 @@ Looking for chat channel docs (WhatsApp/Telegram/Discord/Slack/Mattermost (plugi ## Quick start -1. Authenticate with the provider (usually via `openclaw setup --wizard`). +1. Authenticate with the provider (usually via `openclaw onboard`). 2. Set the default model: ```json5 diff --git a/docs/providers/kilocode.md b/docs/providers/kilocode.md index b3d75e64bcf..15f8e4c2b7c 100644 --- a/docs/providers/kilocode.md +++ b/docs/providers/kilocode.md @@ -19,7 +19,7 @@ endpoint and API key. It is OpenAI-compatible, so most OpenAI SDKs work by switc ## CLI setup ```bash -openclaw setup --wizard --kilocode-api-key +openclaw onboard --kilocode-api-key ``` Or set the environment variable: diff --git a/docs/providers/litellm.md b/docs/providers/litellm.md index d96e1bb795c..51ad0d599f8 100644 --- a/docs/providers/litellm.md +++ b/docs/providers/litellm.md @@ -22,7 +22,7 @@ read_when: ### Via onboarding ```bash -openclaw setup --wizard --auth-choice litellm-api-key +openclaw onboard --auth-choice litellm-api-key ``` ### Manual setup diff --git a/docs/providers/minimax.md b/docs/providers/minimax.md index 7a39111f6c2..0d3635352cc 100644 --- a/docs/providers/minimax.md +++ b/docs/providers/minimax.md @@ -44,7 +44,7 @@ Enable the bundled OAuth plugin and authenticate: ```bash openclaw plugins enable minimax # skip if already loaded. openclaw gateway restart # restart if gateway is already running -openclaw setup --wizard --auth-choice minimax-portal +openclaw onboard --auth-choice minimax-portal ``` You will be prompted to select an endpoint: diff --git a/docs/providers/mistral.md b/docs/providers/mistral.md index 60a9e82853d..44e594abf21 100644 --- a/docs/providers/mistral.md +++ b/docs/providers/mistral.md @@ -15,9 +15,9 @@ Mistral can also be used for memory embeddings (`memorySearch.provider = "mistra ## CLI setup ```bash -openclaw setup --wizard --auth-choice mistral-api-key +openclaw onboard --auth-choice mistral-api-key # or non-interactive -openclaw setup --wizard --mistral-api-key "$MISTRAL_API_KEY" +openclaw onboard --mistral-api-key "$MISTRAL_API_KEY" ``` ## Config snippet (LLM provider) diff --git a/docs/providers/models.md b/docs/providers/models.md index 0bbff47c51e..a117d286051 100644 --- a/docs/providers/models.md +++ b/docs/providers/models.md @@ -13,7 +13,7 @@ model as `provider/model`. ## Quick start (two steps) -1. Authenticate with the provider (usually via `openclaw setup --wizard`). +1. Authenticate with the provider (usually via `openclaw onboard`). 2. Set the default model: ```json5 diff --git a/docs/providers/moonshot.md b/docs/providers/moonshot.md index de21a6ffb0a..daf9c881de5 100644 --- a/docs/providers/moonshot.md +++ b/docs/providers/moonshot.md @@ -26,13 +26,13 @@ Current Kimi K2 model IDs: [//]: # "moonshot-kimi-k2-ids:end" ```bash -openclaw setup --wizard --auth-choice moonshot-api-key +openclaw onboard --auth-choice moonshot-api-key ``` Kimi Coding: ```bash -openclaw setup --wizard --auth-choice kimi-code-api-key +openclaw onboard --auth-choice kimi-code-api-key ``` Note: Moonshot and Kimi Coding are separate providers. Keys are not interchangeable, endpoints differ, and model refs differ (Moonshot uses `moonshot/...`, Kimi Coding uses `kimi-coding/...`). diff --git a/docs/providers/nvidia.md b/docs/providers/nvidia.md index 2708d88db96..693a51db9b3 100644 --- a/docs/providers/nvidia.md +++ b/docs/providers/nvidia.md @@ -16,7 +16,7 @@ Export the key once, then run onboarding and set an NVIDIA model: ```bash export NVIDIA_API_KEY="nvapi-..." -openclaw setup --wizard --auth-choice skip +openclaw onboard --auth-choice skip openclaw models set nvidia/nvidia/llama-3.1-nemotron-70b-instruct ``` diff --git a/docs/providers/ollama.md b/docs/providers/ollama.md index db36f90a2da..5a1eb2bd27e 100644 --- a/docs/providers/ollama.md +++ b/docs/providers/ollama.md @@ -21,7 +21,7 @@ Ollama is a local LLM runtime that makes it easy to run open-source models on yo The fastest way to set up Ollama is through the setup wizard: ```bash -openclaw setup --wizard +openclaw onboard ``` Select **Ollama** from the provider list. The wizard will: @@ -35,7 +35,7 @@ Select **Ollama** from the provider list. The wizard will: Non-interactive mode is also supported: ```bash -openclaw setup --wizard --non-interactive \ +openclaw onboard --non-interactive \ --auth-choice ollama \ --accept-risk ``` @@ -43,7 +43,7 @@ openclaw setup --wizard --non-interactive \ Optionally specify a custom base URL or model: ```bash -openclaw setup --wizard --non-interactive \ +openclaw onboard --non-interactive \ --auth-choice ollama \ --custom-base-url "http://ollama-host:11434" \ --custom-model-id "qwen3.5:27b" \ @@ -73,7 +73,7 @@ ollama signin 4. Run onboarding and choose `Ollama`: ```bash -openclaw setup --wizard +openclaw onboard ``` - `Local`: local models only diff --git a/docs/providers/openai.md b/docs/providers/openai.md index 4f90d092838..a6a60f8f2ea 100644 --- a/docs/providers/openai.md +++ b/docs/providers/openai.md @@ -20,9 +20,9 @@ Get your API key from the OpenAI dashboard. ### CLI setup ```bash -openclaw setup --wizard --auth-choice openai-api-key +openclaw onboard --auth-choice openai-api-key # or non-interactive -openclaw setup --wizard --openai-api-key "$OPENAI_API_KEY" +openclaw onboard --openai-api-key "$OPENAI_API_KEY" ``` ### Config snippet @@ -52,7 +52,7 @@ Codex cloud requires ChatGPT sign-in, while the Codex CLI supports ChatGPT or AP ```bash # Run Codex OAuth in the wizard -openclaw setup --wizard --auth-choice openai-codex +openclaw onboard --auth-choice openai-codex # Or run OAuth directly openclaw models auth login --provider openai-codex diff --git a/docs/providers/opencode-go.md b/docs/providers/opencode-go.md index 2d826712977..4552e916beb 100644 --- a/docs/providers/opencode-go.md +++ b/docs/providers/opencode-go.md @@ -21,9 +21,9 @@ provider id `opencode-go` so upstream per-model routing stays correct. ## CLI setup ```bash -openclaw setup --wizard --auth-choice opencode-go +openclaw onboard --auth-choice opencode-go # or non-interactive -openclaw setup --wizard --opencode-go-api-key "$OPENCODE_API_KEY" +openclaw onboard --opencode-go-api-key "$OPENCODE_API_KEY" ``` ## Config snippet diff --git a/docs/providers/opencode.md b/docs/providers/opencode.md index 98eb2cfcbe0..da44e5154c0 100644 --- a/docs/providers/opencode.md +++ b/docs/providers/opencode.md @@ -22,15 +22,15 @@ as one OpenCode setup. ### Zen catalog ```bash -openclaw setup --wizard --auth-choice opencode-zen -openclaw setup --wizard --opencode-zen-api-key "$OPENCODE_API_KEY" +openclaw onboard --auth-choice opencode-zen +openclaw onboard --opencode-zen-api-key "$OPENCODE_API_KEY" ``` ### Go catalog ```bash -openclaw setup --wizard --auth-choice opencode-go -openclaw setup --wizard --opencode-go-api-key "$OPENCODE_API_KEY" +openclaw onboard --auth-choice opencode-go +openclaw onboard --opencode-go-api-key "$OPENCODE_API_KEY" ``` ## Config snippet diff --git a/docs/providers/openrouter.md b/docs/providers/openrouter.md index 4da33dbb1bc..5a9023481be 100644 --- a/docs/providers/openrouter.md +++ b/docs/providers/openrouter.md @@ -14,7 +14,7 @@ endpoint and API key. It is OpenAI-compatible, so most OpenAI SDKs work by switc ## CLI setup ```bash -openclaw setup --wizard --auth-choice apiKey --token-provider openrouter --token "$OPENROUTER_API_KEY" +openclaw onboard --auth-choice apiKey --token-provider openrouter --token "$OPENROUTER_API_KEY" ``` ## Config snippet diff --git a/docs/providers/qianfan.md b/docs/providers/qianfan.md index 9784dcc64dd..1e80dafb26b 100644 --- a/docs/providers/qianfan.md +++ b/docs/providers/qianfan.md @@ -27,7 +27,7 @@ endpoint and API key. It is OpenAI-compatible, so most OpenAI SDKs work by switc ## CLI setup ```bash -openclaw setup --wizard --auth-choice qianfan-api-key +openclaw onboard --auth-choice qianfan-api-key ``` ## Related Documentation diff --git a/docs/providers/sglang.md b/docs/providers/sglang.md index 96d33d5e767..ce66950c0c3 100644 --- a/docs/providers/sglang.md +++ b/docs/providers/sglang.md @@ -33,7 +33,7 @@ export SGLANG_API_KEY="sglang-local" 3. Run onboarding and choose `SGLang`, or set a model directly: ```bash -openclaw setup --wizard +openclaw onboard ``` ```json5 diff --git a/docs/providers/synthetic.md b/docs/providers/synthetic.md index 0e662320984..ae406a0e390 100644 --- a/docs/providers/synthetic.md +++ b/docs/providers/synthetic.md @@ -17,7 +17,7 @@ Synthetic exposes Anthropic-compatible endpoints. OpenClaw registers it as the 2. Run onboarding: ```bash -openclaw setup --wizard --auth-choice synthetic-api-key +openclaw onboard --auth-choice synthetic-api-key ``` The default model is set to: diff --git a/docs/providers/together.md b/docs/providers/together.md index e93224e5da3..62bab43a204 100644 --- a/docs/providers/together.md +++ b/docs/providers/together.md @@ -18,7 +18,7 @@ The [Together AI](https://together.ai) provides access to leading open-source mo 1. Set the API key (recommended: store it for the Gateway): ```bash -openclaw setup --wizard --auth-choice together-api-key +openclaw onboard --auth-choice together-api-key ``` 2. Set a default model: @@ -36,7 +36,7 @@ openclaw setup --wizard --auth-choice together-api-key ## Non-interactive example ```bash -openclaw setup --wizard --non-interactive \ +openclaw onboard --non-interactive \ --mode local \ --auth-choice together-api-key \ --together-api-key "$TOGETHER_API_KEY" diff --git a/docs/providers/venice.md b/docs/providers/venice.md index a793239eb6f..520cf22d82b 100644 --- a/docs/providers/venice.md +++ b/docs/providers/venice.md @@ -58,7 +58,7 @@ export VENICE_API_KEY="vapi_xxxxxxxxxxxx" **Option B: Interactive Setup (Recommended)** ```bash -openclaw setup --wizard --auth-choice venice-api-key +openclaw onboard --auth-choice venice-api-key ``` This will: @@ -71,7 +71,7 @@ This will: **Option C: Non-interactive** ```bash -openclaw setup --wizard --non-interactive \ +openclaw onboard --non-interactive \ --auth-choice venice-api-key \ --venice-api-key "vapi_xxxxxxxxxxxx" ``` diff --git a/docs/providers/vercel-ai-gateway.md b/docs/providers/vercel-ai-gateway.md index 55acf7f2ba7..f76e2b51bb5 100644 --- a/docs/providers/vercel-ai-gateway.md +++ b/docs/providers/vercel-ai-gateway.md @@ -21,7 +21,7 @@ The [Vercel AI Gateway](https://vercel.com/ai-gateway) provides a unified API to 1. Set the API key (recommended: store it for the Gateway): ```bash -openclaw setup --wizard --auth-choice ai-gateway-api-key +openclaw onboard --auth-choice ai-gateway-api-key ``` 2. Set a default model: @@ -39,7 +39,7 @@ openclaw setup --wizard --auth-choice ai-gateway-api-key ## Non-interactive example ```bash -openclaw setup --wizard --non-interactive \ +openclaw onboard --non-interactive \ --mode local \ --auth-choice ai-gateway-api-key \ --ai-gateway-api-key "$AI_GATEWAY_API_KEY" diff --git a/docs/providers/xiaomi.md b/docs/providers/xiaomi.md index ec6ec043125..da1cf7fe38a 100644 --- a/docs/providers/xiaomi.md +++ b/docs/providers/xiaomi.md @@ -22,9 +22,9 @@ the `xiaomi` provider with a Xiaomi MiMo API key. ## CLI setup ```bash -openclaw setup --wizard --auth-choice xiaomi-api-key +openclaw onboard --auth-choice xiaomi-api-key # or non-interactive -openclaw setup --wizard --auth-choice xiaomi-api-key --xiaomi-api-key "$XIAOMI_API_KEY" +openclaw onboard --auth-choice xiaomi-api-key --xiaomi-api-key "$XIAOMI_API_KEY" ``` ## Config snippet diff --git a/docs/providers/zai.md b/docs/providers/zai.md index 86a0b3c6878..6f3aea27020 100644 --- a/docs/providers/zai.md +++ b/docs/providers/zai.md @@ -16,16 +16,16 @@ with a Z.AI API key. ```bash # Coding Plan Global, recommended for Coding Plan users -openclaw setup --wizard --auth-choice zai-coding-global +openclaw onboard --auth-choice zai-coding-global # Coding Plan CN (China region), recommended for Coding Plan users -openclaw setup --wizard --auth-choice zai-coding-cn +openclaw onboard --auth-choice zai-coding-cn # General API -openclaw setup --wizard --auth-choice zai-global +openclaw onboard --auth-choice zai-global # General API CN (China region) -openclaw setup --wizard --auth-choice zai-cn +openclaw onboard --auth-choice zai-cn ``` ## Config snippet diff --git a/docs/refactor/plugin-sdk.md b/docs/refactor/plugin-sdk.md index a6a10cf9472..5a630982a97 100644 --- a/docs/refactor/plugin-sdk.md +++ b/docs/refactor/plugin-sdk.md @@ -212,3 +212,27 @@ Notes: - External plugins can be developed and updated without core source access. Related docs: [Plugins](/tools/plugin), [Channels](/channels/index), [Configuration](/gateway/configuration). + +## Implemented channel-owned seams + +Recent refactor work widened the channel plugin contract so core can stop owning +channel-specific UX and routing behavior: + +- `messaging.buildCrossContextComponents`: channel-owned cross-context UI markers + (for example Discord components v2 containers) +- `messaging.enableInteractiveReplies`: channel-owned reply normalization toggles + (for example Slack interactive replies) +- `messaging.resolveOutboundSessionRoute`: channel-owned outbound session routing +- `status.formatCapabilitiesProbe` / `status.buildCapabilitiesDiagnostics`: channel-owned + `/channels capabilities` probe display and extra audits/scopes +- `threading.resolveAutoThreadId`: channel-owned same-conversation auto-threading +- `threading.resolveReplyTransport`: channel-owned reply-vs-thread delivery mapping +- `actions.requiresTrustedRequesterSender`: channel-owned privileged action trust gates +- `execApprovals.*`: channel-owned exec approval surface state, forwarding suppression, + pending payload UX, and pre-delivery hooks +- `lifecycle.onAccountConfigChanged` / `lifecycle.onAccountRemoved`: channel-owned cleanup on + config mutation/removal +- `allowlist.supportsScope`: channel-owned allowlist scope advertisement + +These hooks should be preferred over new `channel === "discord"` / `telegram` +branches in shared core flows. diff --git a/docs/reference/wizard.md b/docs/reference/wizard.md index b52aa74086d..5bfa3da7f9f 100644 --- a/docs/reference/wizard.md +++ b/docs/reference/wizard.md @@ -2,7 +2,7 @@ summary: "Full reference for the CLI setup wizard: every step, flag, and config field" read_when: - Looking up a specific wizard step or flag - - Automating setup with non-interactive mode + - Automating onboarding with non-interactive mode - Debugging wizard behavior title: "Setup Wizard Reference" sidebarTitle: "Wizard Reference" @@ -10,7 +10,7 @@ sidebarTitle: "Wizard Reference" # Setup Wizard Reference -This is the full reference for the `openclaw setup --wizard` CLI wizard. +This is the full reference for the `openclaw onboard` CLI wizard. For a high-level overview, see [Setup Wizard](/start/wizard). ## Flow details (local mode) @@ -76,11 +76,11 @@ For a high-level overview, see [Setup Wizard](/start/wizard). - In token mode, interactive setup offers: - **Generate/store plaintext token** (default) - **Use SecretRef** (opt-in) - - Quickstart reuses existing `gateway.auth.token` SecretRefs across `env`, `file`, and `exec` providers for setup probe/dashboard bootstrap. - - If that SecretRef is configured but cannot be resolved, setup fails early with a clear fix message instead of silently degrading runtime auth. + - Quickstart reuses existing `gateway.auth.token` SecretRefs across `env`, `file`, and `exec` providers for onboarding probe/dashboard bootstrap. + - If that SecretRef is configured but cannot be resolved, onboarding fails early with a clear fix message instead of silently degrading runtime auth. - In password mode, interactive setup also supports plaintext or SecretRef storage. - Non-interactive token SecretRef path: `--gateway-token-ref-env `. - - Requires a non-empty env var in the setup process environment. + - Requires a non-empty env var in the onboarding process environment. - Cannot be combined with `--gateway-token`. - Disable auth only if you fully trust every local process. - Non‑loopback binds still require auth. @@ -137,7 +137,7 @@ If the Control UI assets are missing, the wizard attempts to build them; fallbac Use `--non-interactive` to automate or script onboarding: ```bash -openclaw setup --wizard --non-interactive \ +openclaw onboard --non-interactive \ --mode local \ --auth-choice apiKey \ --anthropic-api-key "$ANTHROPIC_API_KEY" \ @@ -154,7 +154,7 @@ Gateway token SecretRef in non-interactive mode: ```bash export OPENCLAW_GATEWAY_TOKEN="your-token" -openclaw setup --wizard --non-interactive \ +openclaw onboard --non-interactive \ --mode local \ --auth-choice skip \ --gateway-auth token \ diff --git a/docs/start/getting-started.md b/docs/start/getting-started.md index af779afbe42..3fc64e5087d 100644 --- a/docs/start/getting-started.md +++ b/docs/start/getting-started.md @@ -54,7 +54,7 @@ Check your Node version with `node --version` if you are unsure. ```bash - openclaw setup --wizard --install-daemon + openclaw onboard --install-daemon ``` The wizard configures auth, gateway settings, and optional channels. diff --git a/docs/start/onboarding-overview.md b/docs/start/onboarding-overview.md index c2147252d2b..1e94a4db64a 100644 --- a/docs/start/onboarding-overview.md +++ b/docs/start/onboarding-overview.md @@ -1,18 +1,18 @@ --- -summary: "Overview of OpenClaw setup options and flows" +summary: "Overview of OpenClaw onboarding options and flows" read_when: - - Choosing a setup path + - Choosing an onboarding path - Setting up a new environment -title: "Setup Overview" -sidebarTitle: "Setup Overview" +title: "Onboarding Overview" +sidebarTitle: "Onboarding Overview" --- -# Setup Overview +# Onboarding Overview -OpenClaw supports multiple setup paths depending on where the Gateway runs +OpenClaw supports multiple onboarding paths depending on where the Gateway runs and how you prefer to configure providers. -## Choose your setup path +## Choose your onboarding path - **CLI wizard** for macOS, Linux, and Windows (via WSL2). - **macOS app** for a guided first run on Apple silicon or Intel Macs. @@ -22,14 +22,14 @@ and how you prefer to configure providers. Run the wizard in a terminal: ```bash -openclaw setup --wizard +openclaw onboard ``` Use the CLI wizard when you want full control of the Gateway, workspace, channels, and skills. Docs: - [Setup Wizard (CLI)](/start/wizard) -- [`openclaw setup --wizard` command](/cli/setup) +- [`openclaw onboard` command](/cli/onboard) ## macOS app onboarding @@ -48,4 +48,4 @@ CLI wizard. You will be asked to: - Provide a model ID and optional alias. - Choose an Endpoint ID so multiple custom endpoints can coexist. -For detailed steps, follow the CLI setup docs above. +For detailed steps, follow the CLI onboarding docs above. diff --git a/docs/start/wizard-cli-automation.md b/docs/start/wizard-cli-automation.md index 17803cefe48..884d49e143b 100644 --- a/docs/start/wizard-cli-automation.md +++ b/docs/start/wizard-cli-automation.md @@ -1,7 +1,7 @@ --- -summary: "Scripted setup wizard and agent setup for the OpenClaw CLI" +summary: "Scripted onboarding and agent setup for the OpenClaw CLI" read_when: - - You are automating setup in scripts or CI + - You are automating onboarding in scripts or CI - You need non-interactive examples for specific providers title: "CLI Automation" sidebarTitle: "CLI automation" @@ -9,7 +9,7 @@ sidebarTitle: "CLI automation" # CLI Automation -Use `--non-interactive` to automate `openclaw setup --wizard`. +Use `--non-interactive` to automate `openclaw onboard`. `--json` does not imply non-interactive mode. Use `--non-interactive` (and `--workspace`) for scripts. @@ -18,7 +18,7 @@ Use `--non-interactive` to automate `openclaw setup --wizard`. ## Baseline non-interactive example ```bash -openclaw setup --wizard --non-interactive \ +openclaw onboard --non-interactive \ --mode local \ --auth-choice apiKey \ --anthropic-api-key "$ANTHROPIC_API_KEY" \ @@ -41,7 +41,7 @@ Passing inline key flags without the matching env var now fails fast. Example: ```bash -openclaw setup --wizard --non-interactive \ +openclaw onboard --non-interactive \ --mode local \ --auth-choice openai-api-key \ --secret-input-mode ref \ @@ -53,7 +53,7 @@ openclaw setup --wizard --non-interactive \ ```bash - openclaw setup --wizard --non-interactive \ + openclaw onboard --non-interactive \ --mode local \ --auth-choice gemini-api-key \ --gemini-api-key "$GEMINI_API_KEY" \ @@ -63,7 +63,7 @@ openclaw setup --wizard --non-interactive \ ```bash - openclaw setup --wizard --non-interactive \ + openclaw onboard --non-interactive \ --mode local \ --auth-choice zai-api-key \ --zai-api-key "$ZAI_API_KEY" \ @@ -73,7 +73,7 @@ openclaw setup --wizard --non-interactive \ ```bash - openclaw setup --wizard --non-interactive \ + openclaw onboard --non-interactive \ --mode local \ --auth-choice ai-gateway-api-key \ --ai-gateway-api-key "$AI_GATEWAY_API_KEY" \ @@ -83,7 +83,7 @@ openclaw setup --wizard --non-interactive \ ```bash - openclaw setup --wizard --non-interactive \ + openclaw onboard --non-interactive \ --mode local \ --auth-choice cloudflare-ai-gateway-api-key \ --cloudflare-ai-gateway-account-id "your-account-id" \ @@ -95,7 +95,7 @@ openclaw setup --wizard --non-interactive \ ```bash - openclaw setup --wizard --non-interactive \ + openclaw onboard --non-interactive \ --mode local \ --auth-choice moonshot-api-key \ --moonshot-api-key "$MOONSHOT_API_KEY" \ @@ -105,7 +105,7 @@ openclaw setup --wizard --non-interactive \ ```bash - openclaw setup --wizard --non-interactive \ + openclaw onboard --non-interactive \ --mode local \ --auth-choice mistral-api-key \ --mistral-api-key "$MISTRAL_API_KEY" \ @@ -115,7 +115,7 @@ openclaw setup --wizard --non-interactive \ ```bash - openclaw setup --wizard --non-interactive \ + openclaw onboard --non-interactive \ --mode local \ --auth-choice synthetic-api-key \ --synthetic-api-key "$SYNTHETIC_API_KEY" \ @@ -125,7 +125,7 @@ openclaw setup --wizard --non-interactive \ ```bash - openclaw setup --wizard --non-interactive \ + openclaw onboard --non-interactive \ --mode local \ --auth-choice opencode-zen \ --opencode-zen-api-key "$OPENCODE_API_KEY" \ @@ -136,7 +136,7 @@ openclaw setup --wizard --non-interactive \ ```bash - openclaw setup --wizard --non-interactive \ + openclaw onboard --non-interactive \ --mode local \ --auth-choice ollama \ --custom-model-id "qwen3.5:27b" \ @@ -147,7 +147,7 @@ openclaw setup --wizard --non-interactive \ ```bash - openclaw setup --wizard --non-interactive \ + openclaw onboard --non-interactive \ --mode local \ --auth-choice custom-api-key \ --custom-base-url "https://llm.example.com/v1" \ @@ -165,7 +165,7 @@ openclaw setup --wizard --non-interactive \ ```bash export CUSTOM_API_KEY="your-key" - openclaw setup --wizard --non-interactive \ + openclaw onboard --non-interactive \ --mode local \ --auth-choice custom-api-key \ --custom-base-url "https://llm.example.com/v1" \ @@ -212,4 +212,4 @@ Notes: - Onboarding hub: [Setup Wizard (CLI)](/start/wizard) - Full reference: [CLI Setup Reference](/start/wizard-cli-reference) -- Command reference: [`openclaw setup --wizard`](/cli/setup) +- Command reference: [`openclaw onboard`](/cli/onboard) diff --git a/docs/start/wizard-cli-reference.md b/docs/start/wizard-cli-reference.md index 2a2bac76528..36bd836a13f 100644 --- a/docs/start/wizard-cli-reference.md +++ b/docs/start/wizard-cli-reference.md @@ -1,15 +1,15 @@ --- summary: "Complete reference for CLI setup flow, auth/model setup, outputs, and internals" read_when: - - You need detailed behavior for `openclaw setup --wizard` - - You are debugging setup results or integrating setup clients + - You need detailed behavior for openclaw onboard + - You are debugging onboarding results or integrating onboarding clients title: "CLI Setup Reference" sidebarTitle: "CLI reference" --- # CLI Setup Reference -This page is the full reference for `openclaw setup --wizard`. +This page is the full reference for `openclaw onboard`. For the short guide, see [Setup Wizard (CLI)](/start/wizard). ## What the wizard does @@ -56,7 +56,7 @@ It does not install or modify anything on the remote host. - **Use SecretRef** (opt-in) - In password mode, interactive setup also supports plaintext or SecretRef storage. - Non-interactive token SecretRef path: `--gateway-token-ref-env `. - - Requires a non-empty env var in the setup process environment. + - Requires a non-empty env var in the onboarding process environment. - Cannot be combined with `--gateway-token`. - Disable auth only if you fully trust every local process. - Non-loopback binds still require auth. @@ -220,20 +220,20 @@ Credential and profile paths: Credential storage mode: -- Default setup behavior persists API keys as plaintext values in auth profiles. +- Default onboarding behavior persists API keys as plaintext values in auth profiles. - `--secret-input-mode ref` enables reference mode instead of plaintext key storage. In interactive setup, you can choose either: - environment variable ref (for example `keyRef: { source: "env", provider: "default", id: "OPENAI_API_KEY" }`) - configured provider ref (`file` or `exec`) with provider alias + id - Interactive reference mode runs a fast preflight validation before saving. - - Env refs: validates variable name + non-empty value in the current setup environment. + - Env refs: validates variable name + non-empty value in the current onboarding environment. - Provider refs: validates provider config and resolves the requested id. - - If preflight fails, setup shows the error and lets you retry. + - If preflight fails, onboarding shows the error and lets you retry. - In non-interactive mode, `--secret-input-mode ref` is env-backed only. - - Set the provider env var in the setup process environment. - - Inline key flags (for example `--openai-api-key`) require that env var to be set; otherwise setup fails fast. + - Set the provider env var in the onboarding process environment. + - Inline key flags (for example `--openai-api-key`) require that env var to be set; otherwise onboarding fails fast. - For custom providers, non-interactive `ref` mode stores `models.providers..apiKey` as `{ source: "env", provider: "default", id: "CUSTOM_API_KEY" }`. - - In that custom-provider case, `--custom-api-key` requires `CUSTOM_API_KEY` to be set; otherwise setup fails fast. + - In that custom-provider case, `--custom-api-key` requires `CUSTOM_API_KEY` to be set; otherwise onboarding fails fast. - Gateway auth credentials support plaintext and SecretRef choices in interactive setup: - Token mode: **Generate/store plaintext token** (default) or **Use SecretRef**. - Password mode: plaintext or SecretRef. @@ -252,9 +252,9 @@ Typical fields in `~/.openclaw/openclaw.json`: - `agents.defaults.workspace` - `agents.defaults.model` / `models.providers` (if Minimax chosen) -- `tools.profile` (local setup defaults to `"coding"` when unset; existing explicit values are preserved) +- `tools.profile` (local onboarding defaults to `"coding"` when unset; existing explicit values are preserved) - `gateway.*` (mode, bind, auth, tailscale) -- `session.dmScope` (local setup defaults this to `per-channel-peer` when unset; existing explicit values are preserved) +- `session.dmScope` (local onboarding defaults this to `per-channel-peer` when unset; existing explicit values are preserved) - `channels.telegram.botToken`, `channels.discord.token`, `channels.signal.*`, `channels.imessage.*` - Channel allowlists (Slack, Discord, Matrix, Microsoft Teams) when you opt in during prompts (names resolve to IDs when possible) - `skills.install.nodeManager` @@ -296,4 +296,4 @@ Signal setup behavior: - Onboarding hub: [Setup Wizard (CLI)](/start/wizard) - Automation and scripts: [CLI Automation](/start/wizard-cli-automation) -- Command reference: [`openclaw setup --wizard`](/cli/setup) +- Command reference: [`openclaw onboard`](/cli/onboard) diff --git a/docs/start/wizard.md b/docs/start/wizard.md index fe887ea9a4f..7bbe9df64cf 100644 --- a/docs/start/wizard.md +++ b/docs/start/wizard.md @@ -4,7 +4,7 @@ read_when: - Running or configuring the setup wizard - Setting up a new machine title: "Setup Wizard (CLI)" -sidebarTitle: "Setup: CLI" +sidebarTitle: "Onboarding: CLI" --- # Setup Wizard (CLI) @@ -15,7 +15,7 @@ It configures a local Gateway or a remote Gateway connection, plus channels, ski and workspace defaults in one guided flow. ```bash -openclaw setup --wizard +openclaw onboard ``` @@ -52,7 +52,7 @@ The wizard starts with **QuickStart** (defaults) vs **Advanced** (full control). - Gateway port **18789** - Gateway auth **Token** (auto‑generated, even on loopback) - Tool policy default for new local setups: `tools.profile: "coding"` (existing explicit profile is preserved) - - DM isolation default: local setup writes `session.dmScope: "per-channel-peer"` when unset. Details: [CLI Setup Reference](/start/wizard-cli-reference#outputs-and-internals) + - DM isolation default: local onboarding writes `session.dmScope: "per-channel-peer"` when unset. Details: [CLI Setup Reference](/start/wizard-cli-reference#outputs-and-internals) - Tailscale exposure **Off** - Telegram + WhatsApp DMs default to **allowlist** (you'll be prompted for your phone number) @@ -119,7 +119,7 @@ For the deeper technical reference, including RPC details, see ## Related docs -- CLI command reference: [`openclaw setup`](/cli/setup) -- Setup overview: [Setup Overview](/start/onboarding-overview) +- CLI command reference: [`openclaw onboard`](/cli/onboard) +- Onboarding overview: [Onboarding Overview](/start/onboarding-overview) - macOS app onboarding: [Onboarding](/start/onboarding) - Agent first-run ritual: [Agent Bootstrapping](/start/bootstrapping) diff --git a/docs/tools/browser-linux-troubleshooting.md b/docs/tools/browser-linux-troubleshooting.md index 6f9940c1c67..2a5196c3739 100644 --- a/docs/tools/browser-linux-troubleshooting.md +++ b/docs/tools/browser-linux-troubleshooting.md @@ -121,19 +121,18 @@ curl -s http://127.0.0.1:18791/tabs | `browser.attachOnly` | Don't launch browser, only attach to existing | `false` | | `browser.cdpPort` | Chrome DevTools Protocol port | `18800` | -### Problem: "Chrome extension relay is running, but no tab is connected" +### Problem: "No Chrome tabs found for profile=\"user\"" -You're using an extension relay profile. It expects the OpenClaw -browser extension to be attached to a live tab. +You're using an `existing-session` / Chrome MCP profile. OpenClaw can see local Chrome, +but there are no open tabs available to attach to. Fix options: 1. **Use the managed browser:** `openclaw browser start --browser-profile openclaw` (or set `browser.defaultProfile: "openclaw"`). -2. **Use the extension relay:** install the extension, open a tab, and click the - OpenClaw extension icon to attach it. +2. **Use Chrome MCP:** make sure local Chrome is running with at least one open tab, then retry with `--browser-profile user`. Notes: -- The `chrome-relay` profile uses your **system default Chromium browser** when possible. +- `user` is host-only. For Linux servers, containers, or remote hosts, prefer CDP profiles. - Local `openclaw` profiles auto-assign `cdpPort`/`cdpUrl`; only set those for remote CDP. diff --git a/docs/tools/browser-login.md b/docs/tools/browser-login.md index d570b3b2e87..52135a80673 100644 --- a/docs/tools/browser-login.md +++ b/docs/tools/browser-login.md @@ -24,7 +24,6 @@ For agent browser tool calls: - Default choice: the agent should use its isolated `openclaw` browser. - Use `profile="user"` only when existing logged-in sessions matter and the user is at the computer to click/approve any attach prompt. -- Use `profile="chrome-relay"` only for the Chrome extension / toolbar-button attach flow. - If you have multiple user-browser profiles, specify the profile explicitly instead of guessing. Two easy ways to access it: diff --git a/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md b/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md index 2e7844860aa..6824cee6788 100644 --- a/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md +++ b/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md @@ -1,9 +1,9 @@ --- -summary: "Troubleshoot WSL2 Gateway + Windows Chrome remote CDP and extension-relay setups in layers" +summary: "Troubleshoot WSL2 Gateway + Windows Chrome remote CDP in layers" read_when: - Running OpenClaw Gateway in WSL2 while Chrome lives on Windows - Seeing overlapping browser/control-ui errors across WSL2 and Windows - - Deciding between raw remote CDP and the Chrome extension relay in split-host setups + - Deciding between host-local Chrome MCP and raw remote CDP in split-host setups title: "WSL2 + Windows + remote Chrome CDP troubleshooting" --- @@ -21,27 +21,27 @@ It also covers the layered failure pattern from [issue #39369](https://github.co You have two valid patterns: -### Option 1: Raw remote CDP +### Option 1: Raw remote CDP from WSL2 to Windows Use a remote browser profile that points from WSL2 to a Windows Chrome CDP endpoint. Choose this when: -- you only need browser control -- you are comfortable exposing Chrome remote debugging to WSL2 -- you do not need the Chrome extension relay +- the Gateway stays inside WSL2 +- Chrome runs on Windows +- you need browser control to cross the WSL2/Windows boundary -### Option 2: Chrome extension relay +### Option 2: Host-local Chrome MCP -Use the built-in `chrome-relay` profile plus the OpenClaw Chrome extension. +Use `existing-session` / `user` only when the Gateway itself runs on the same host as Chrome. Choose this when: -- you want to attach to an existing Windows Chrome tab with the toolbar button -- you want extension-based control instead of raw `--remote-debugging-port` -- the relay itself must be reachable across the WSL2/Windows boundary +- OpenClaw and Chrome are on the same machine +- you want the local signed-in browser state +- you do not need cross-host browser transport -If you use the extension relay across namespaces, `browser.relayBindHost` is the important setting introduced in [Browser](/tools/browser) and [Chrome extension](/tools/chrome-extension). +For WSL2 Gateway + Windows Chrome, prefer raw remote CDP. Chrome MCP is host-local, not a WSL2-to-Windows bridge. ## Working architecture @@ -62,7 +62,6 @@ Several failures can overlap: - `gateway.controlUi.allowedOrigins` does not match the page origin - token or pairing is missing - the browser profile points at the wrong address -- the extension relay is still loopback-only when you actually need cross-namespace access Because of that, fixing one layer can still leave a different error visible. @@ -145,31 +144,7 @@ Notes: - keep `attachOnly: true` for externally managed browsers - test the same URL with `curl` before expecting OpenClaw to succeed -### Layer 4: If you use the Chrome extension relay instead - -If the browser machine and the Gateway are separated by a namespace boundary, the relay may need a non-loopback bind address. - -Example: - -```json5 -{ - browser: { - enabled: true, - defaultProfile: "chrome-relay", - relayBindHost: "0.0.0.0", - }, -} -``` - -Use this only when needed: - -- default behavior is safer because the relay stays loopback-only -- `0.0.0.0` expands exposure surface -- keep Gateway auth, node pairing, and the surrounding network private - -If you do not need the extension relay, prefer the raw remote CDP profile above. - -### Layer 5: Verify the Control UI layer separately +### Layer 4: Verify the Control UI layer separately Open the UI from Windows: @@ -185,7 +160,7 @@ Helpful page: - [Control UI](/web/control-ui) -### Layer 6: Verify end-to-end browser control +### Layer 5: Verify end-to-end browser control From WSL2: @@ -194,12 +169,6 @@ openclaw browser open https://example.com --browser-profile remote openclaw browser tabs --browser-profile remote ``` -For the extension relay: - -```bash -openclaw browser tabs --browser-profile chrome-relay -``` - Good result: - the tab opens in Windows Chrome @@ -220,8 +189,8 @@ Treat each message as a layer-specific clue: - WSL2 cannot reach the configured `cdpUrl` - `gateway timeout after 1500ms` - often still CDP reachability or a slow/unreachable remote endpoint -- `Chrome extension relay is running, but no tab is connected` - - extension relay profile selected, but no attached tab exists yet +- `No Chrome tabs found for profile="user"` + - local Chrome MCP profile selected where no host-local tabs are available ## Fast triage checklist @@ -229,11 +198,11 @@ Treat each message as a layer-specific clue: 2. WSL2: does `curl http://WINDOWS_HOST_OR_IP:9222/json/version` work? 3. OpenClaw config: does `browser.profiles..cdpUrl` use that exact WSL2-reachable address? 4. Control UI: are you opening `http://127.0.0.1:18789/` instead of a LAN IP? -5. Extension relay only: do you actually need `browser.relayBindHost`, and if so is it set explicitly? +5. Are you trying to use `existing-session` across WSL2 and Windows instead of raw remote CDP? ## Practical takeaway -The setup is usually viable. The hard part is that browser transport, Control UI origin security, token/pairing, and extension-relay topology can each fail independently while looking similar from the user side. +The setup is usually viable. The hard part is that browser transport, Control UI origin security, and token/pairing can each fail independently while looking similar from the user side. When in doubt: diff --git a/docs/tools/browser.md b/docs/tools/browser.md index c760c23998c..19ee23a25ca 100644 --- a/docs/tools/browser.md +++ b/docs/tools/browser.md @@ -18,8 +18,7 @@ Beginner view: - Think of it as a **separate, agent-only browser**. - The `openclaw` profile does **not** touch your personal browser profile. - The agent can **open tabs, read pages, click, and type** in a safe lane. -- The built-in `user` profile attaches to your real signed-in Chrome session; - `chrome-relay` is the explicit extension-relay profile. +- The built-in `user` profile attaches to your real signed-in Chrome session via Chrome MCP. ## What you get @@ -43,21 +42,17 @@ openclaw browser --browser-profile openclaw snapshot If you get “Browser disabled”, enable it in config (see below) and restart the Gateway. -## Profiles: `openclaw` vs `user` vs `chrome-relay` +## Profiles: `openclaw` vs `user` - `openclaw`: managed, isolated browser (no extension required). - `user`: built-in Chrome MCP attach profile for your **real signed-in Chrome** session. -- `chrome-relay`: extension relay to your **system browser** (requires the - OpenClaw extension to be attached to a tab). For agent browser tool calls: - Default: use the isolated `openclaw` browser. - Prefer `profile="user"` when existing logged-in sessions matter and the user is at the computer to click/approve any attach prompt. -- Use `profile="chrome-relay"` only when the user explicitly wants the Chrome - extension / toolbar-button attach flow. - `profile` is the explicit override when you want a specific browser mode. Set `browser.defaultProfile: "openclaw"` if you want managed mode by default. @@ -93,11 +88,6 @@ Browser settings live in `~/.openclaw/openclaw.json`. attachOnly: true, color: "#00AA00", }, - "chrome-relay": { - driver: "extension", - cdpUrl: "http://127.0.0.1:18792", - color: "#00AA00", - }, remote: { cdpUrl: "http://10.0.0.42:9222", color: "#00AA00" }, }, }, @@ -107,10 +97,10 @@ Browser settings live in `~/.openclaw/openclaw.json`. Notes: - The browser control service binds to loopback on a port derived from `gateway.port` - (default: `18791`, which is gateway + 2). The relay uses the next port (`18792`). + (default: `18791`, which is gateway + 2). - If you override the Gateway port (`gateway.port` or `OPENCLAW_GATEWAY_PORT`), the derived browser ports shift to stay in the same “family”. -- `cdpUrl` defaults to the relay port when unset. +- `cdpUrl` defaults to the managed local CDP port when unset. - `remoteCdpTimeoutMs` applies to remote (non-loopback) CDP reachability checks. - `remoteCdpHandshakeTimeoutMs` applies to remote CDP WebSocket reachability checks. - Browser navigation/open-tab is SSRF-guarded before navigation and best-effort re-checked on final `http(s)` URL after navigation. @@ -119,7 +109,7 @@ Notes: - `browser.ssrfPolicy.allowPrivateNetwork` remains supported as a legacy alias for compatibility. - `attachOnly: true` means “never launch a local browser; only attach if it is already running.” - `color` + per-profile `color` tint the browser UI so you can see which profile is active. -- Default profile is `openclaw` (OpenClaw-managed standalone browser). Use `defaultProfile: "user"` to opt into the signed-in user browser, or `defaultProfile: "chrome-relay"` for the extension relay. +- Default profile is `openclaw` (OpenClaw-managed standalone browser). Use `defaultProfile: "user"` to opt into the signed-in user browser. - Auto-detect order: system default browser if Chromium-based; otherwise Chrome → Brave → Edge → Chromium → Chrome Canary. - Local `openclaw` profiles auto-assign `cdpPort`/`cdpUrl` — set those only for remote CDP. - `driver: "existing-session"` uses Chrome DevTools MCP instead of raw CDP. Do @@ -287,77 +277,18 @@ OpenClaw supports multiple named profiles (routing configs). Profiles can be: - **openclaw-managed**: a dedicated Chromium-based browser instance with its own user data directory + CDP port - **remote**: an explicit CDP URL (Chromium-based browser running elsewhere) -- **extension relay**: your existing Chrome tab(s) via the local relay + Chrome extension - **existing session**: your existing Chrome profile via Chrome DevTools MCP auto-connect Defaults: - The `openclaw` profile is auto-created if missing. -- The `chrome-relay` profile is built-in for the Chrome extension relay (points at `http://127.0.0.1:18792` by default). -- Existing-session profiles are opt-in; create them with `--driver existing-session`. +- The `user` profile is built-in for Chrome MCP existing-session attach. +- Existing-session profiles are opt-in beyond `user`; create them with `--driver existing-session`. - Local CDP ports allocate from **18800–18899** by default. - Deleting a profile moves its local data directory to Trash. All control endpoints accept `?profile=`; the CLI uses `--browser-profile`. -## Chrome extension relay (use your existing Chrome) - -OpenClaw can also drive **your existing Chrome tabs** (no separate “openclaw” Chrome instance) via a local CDP relay + a Chrome extension. - -Full guide: [Chrome extension](/tools/chrome-extension) - -Flow: - -- The Gateway runs locally (same machine) or a node host runs on the browser machine. -- A local **relay server** listens at a loopback `cdpUrl` (default: `http://127.0.0.1:18792`). -- You click the **OpenClaw Browser Relay** extension icon on a tab to attach (it does not auto-attach). -- The agent controls that tab via the normal `browser` tool, by selecting the right profile. - -If the Gateway runs elsewhere, run a node host on the browser machine so the Gateway can proxy browser actions. - -### Sandboxed sessions - -If the agent session is sandboxed, the `browser` tool may default to `target="sandbox"` (sandbox browser). -Chrome extension relay takeover requires host browser control, so either: - -- run the session unsandboxed, or -- set `agents.defaults.sandbox.browser.allowHostControl: true` and use `target="host"` when calling the tool. - -### Setup - -1. Load the extension (dev/unpacked): - -```bash -openclaw browser extension install -``` - -- Chrome → `chrome://extensions` → enable “Developer mode” -- “Load unpacked” → select the directory printed by `openclaw browser extension path` -- Pin the extension, then click it on the tab you want to control (badge shows `ON`). - -2. Use it: - -- CLI: `openclaw browser --browser-profile chrome-relay tabs` -- Agent tool: `browser` with `profile="chrome-relay"` - -Optional: if you want a different name or relay port, create your own profile: - -```bash -openclaw browser create-profile \ - --name my-chrome \ - --driver extension \ - --cdp-url http://127.0.0.1:18792 \ - --color "#00AA00" -``` - -Notes: - -- This mode relies on Playwright-on-CDP for most operations (screenshots/snapshots/actions). -- Detach by clicking the extension icon again. -- Agent use: prefer `profile="user"` for logged-in sites. Use `profile="chrome-relay"` - only when you specifically want the extension flow. The user must be present - to click the extension and attach the tab. - ## Chrome existing-session via MCP OpenClaw can also attach to a running Chrome profile through the official @@ -404,13 +335,14 @@ What to check if attach does not work: - Chrome is version `144+` - remote debugging is enabled at `chrome://inspect/#remote-debugging` - Chrome showed and you accepted the attach consent prompt +- `openclaw doctor` migrates old extension-based browser config and checks that + Chrome is installed locally with a compatible version, but it cannot enable + Chrome-side remote debugging for you Agent use: - Use `profile="user"` when you need the user’s logged-in browser state. - If you use a custom existing-session profile, pass that explicit profile name. -- Prefer `profile="user"` over `profile="chrome-relay"` unless the user - explicitly wants the extension / attach-tab flow. - Only choose this mode when the user is at the computer to approve the attach prompt. - the Gateway or node host can spawn `npx chrome-devtools-mcp@latest --autoConnect` @@ -427,21 +359,10 @@ Notes: captures from snapshots, but not CSS `--element` selectors. - Existing-session `wait --url` supports exact, substring, and glob patterns like other browser drivers. `wait --load networkidle` is not supported yet. -- Some features still require the extension relay or managed browser path, such - as PDF export and download interception. -- Leave the relay loopback-only by default. If the relay must be reachable from a different network namespace (for example Gateway in WSL2, Chrome on Windows), set `browser.relayBindHost` to an explicit bind address such as `0.0.0.0` while keeping the surrounding network private and authenticated. - -WSL2 / cross-namespace example: - -```json5 -{ - browser: { - enabled: true, - relayBindHost: "0.0.0.0", - defaultProfile: "chrome-relay", - }, -} -``` +- Some features still require the managed browser path, such as PDF export and + download interception. +- Existing-session is host-local. If Chrome lives on a different machine or a + different network namespace, use remote CDP or a node host instead. ## Isolation guarantees @@ -496,7 +417,6 @@ If gateway auth is configured, browser HTTP routes require auth too: Some features (navigate/act/AI snapshot/role snapshot, element screenshots, PDF) require Playwright. If Playwright isn’t installed, those endpoints return a clear 501 error. ARIA snapshots and basic screenshots still work for openclaw-managed Chrome. -For the Chrome extension relay driver, ARIA snapshots and screenshots require Playwright. If you see `Playwright is not available in this gateway build`, install the full Playwright package (not `playwright-core`) and restart the gateway, or reinstall diff --git a/docs/tools/chrome-extension.md b/docs/tools/chrome-extension.md deleted file mode 100644 index 831897b9bde..00000000000 --- a/docs/tools/chrome-extension.md +++ /dev/null @@ -1,203 +0,0 @@ ---- -summary: "Chrome extension: let OpenClaw drive your existing Chrome tab" -read_when: - - You want the agent to drive an existing Chrome tab (toolbar button) - - You need remote Gateway + local browser automation via Tailscale - - You want to understand the security implications of browser takeover -title: "Chrome Extension" ---- - -# Chrome extension (browser relay) - -The OpenClaw Chrome extension lets the agent control your **existing Chrome tabs** (your normal Chrome window) instead of launching a separate openclaw-managed Chrome profile. - -Attach/detach happens via a **single Chrome toolbar button**. - -If you want Chrome’s official DevTools MCP attach flow instead of the OpenClaw -extension relay, use an `existing-session` browser profile instead. See -[Browser](/tools/browser#chrome-existing-session-via-mcp). For Chrome’s own -setup docs, see [Chrome for Developers: Use Chrome DevTools MCP with your -browser session](https://developer.chrome.com/blog/chrome-devtools-mcp-debug-your-browser-session) -and the [Chrome DevTools MCP README](https://github.com/ChromeDevTools/chrome-devtools-mcp). - -## What it is (concept) - -There are three parts: - -- **Browser control service** (Gateway or node): the API the agent/tool calls (via the Gateway) -- **Local relay server** (loopback CDP): bridges between the control server and the extension (`http://127.0.0.1:18792` by default) -- **Chrome MV3 extension**: attaches to the active tab using `chrome.debugger` and pipes CDP messages to the relay - -OpenClaw then controls the attached tab through the normal `browser` tool surface (selecting the right profile). - -## Install / load (unpacked) - -1. Install the extension to a stable local path: - -```bash -openclaw browser extension install -``` - -2. Print the installed extension directory path: - -```bash -openclaw browser extension path -``` - -3. Chrome → `chrome://extensions` - -- Enable “Developer mode” -- “Load unpacked” → select the directory printed above - -4. Pin the extension. - -## Updates (no build step) - -The extension ships inside the OpenClaw release (npm package) as static files. There is no separate “build” step. - -After upgrading OpenClaw: - -- Re-run `openclaw browser extension install` to refresh the installed files under your OpenClaw state directory. -- Chrome → `chrome://extensions` → click “Reload” on the extension. - -## Use it (set gateway token once) - -To use the extension relay, create a browser profile for it: - -Before first attach, open extension Options and set: - -- `Port` (default `18792`) -- `Gateway token` (must match `gateway.auth.token` / `OPENCLAW_GATEWAY_TOKEN`) - -Then create a profile: - -```bash -openclaw browser create-profile \ - --name my-chrome \ - --driver extension \ - --cdp-url http://127.0.0.1:18792 \ - --color "#00AA00" -``` - -Use it: - -- CLI: `openclaw browser --browser-profile my-chrome tabs` -- Agent tool: `browser` with `profile="my-chrome"` - -### Custom Gateway ports - -If you're using a custom gateway port, the extension relay port is automatically derived: - -**Extension Relay Port = Gateway Port + 3** - -Example: if `gateway.port: 19001`, then: - -- Extension relay port: `19004` (gateway + 3) - -Configure the extension to use the derived relay port in the extension Options page. - -## Attach / detach (toolbar button) - -- Open the tab you want OpenClaw to control. -- Click the extension icon. - - Badge shows `ON` when attached. -- Click again to detach. - -## Which tab does it control? - -- It does **not** automatically control “whatever tab you’re looking at”. -- It controls **only the tab(s) you explicitly attached** by clicking the toolbar button. -- To switch: open the other tab and click the extension icon there. - -## Badge + common errors - -- `ON`: attached; OpenClaw can drive that tab. -- `…`: connecting to the local relay. -- `!`: relay not reachable/authenticated (most common: relay server not running, or gateway token missing/wrong). - -If you see `!`: - -- Make sure the Gateway is running locally (default setup), or run a node host on this machine if the Gateway runs elsewhere. -- Open the extension Options page; it validates relay reachability + gateway-token auth. - -## Remote Gateway (use a node host) - -### Local Gateway (same machine as Chrome) — usually **no extra steps** - -If the Gateway runs on the same machine as Chrome, it starts the browser control service on loopback -and auto-starts the relay server. The extension talks to the local relay; the CLI/tool calls go to the Gateway. - -### Remote Gateway (Gateway runs elsewhere) — **run a node host** - -If your Gateway runs on another machine, start a node host on the machine that runs Chrome. -The Gateway will proxy browser actions to that node; the extension + relay stay local to the browser machine. - -If multiple nodes are connected, pin one with `gateway.nodes.browser.node` or set `gateway.nodes.browser.mode`. - -## Sandboxing (tool containers) - -If your agent session is sandboxed (`agents.defaults.sandbox.mode != "off"`), the `browser` tool can be restricted: - -- By default, sandboxed sessions often target the **sandbox browser** (`target="sandbox"`), not your host Chrome. -- Chrome extension relay takeover requires controlling the **host** browser control server. - -Options: - -- Easiest: use the extension from a **non-sandboxed** session/agent. -- Or allow host browser control for sandboxed sessions: - -```json5 -{ - agents: { - defaults: { - sandbox: { - browser: { - allowHostControl: true, - }, - }, - }, - }, -} -``` - -Then ensure the tool isn’t denied by tool policy, and (if needed) call `browser` with `target="host"`. - -Debugging: `openclaw sandbox explain` - -## Remote access tips - -- Keep the Gateway and node host on the same tailnet; avoid exposing relay ports to LAN or public Internet. -- Pair nodes intentionally; disable browser proxy routing if you don’t want remote control (`gateway.nodes.browser.mode="off"`). -- Leave the relay on loopback unless you have a real cross-namespace need. For WSL2 or similar split-host setups, set `browser.relayBindHost` to an explicit bind address such as `0.0.0.0`, then keep access constrained with Gateway auth, node pairing, and a private network. - -## How “extension path” works - -`openclaw browser extension path` prints the **installed** on-disk directory containing the extension files. - -The CLI intentionally does **not** print a `node_modules` path. Always run `openclaw browser extension install` first to copy the extension to a stable location under your OpenClaw state directory. - -If you move or delete that install directory, Chrome will mark the extension as broken until you reload it from a valid path. - -## Security implications (read this) - -This is powerful and risky. Treat it like giving the model “hands on your browser”. - -- The extension uses Chrome’s debugger API (`chrome.debugger`). When attached, the model can: - - click/type/navigate in that tab - - read page content - - access whatever the tab’s logged-in session can access -- **This is not isolated** like the dedicated openclaw-managed profile. - - If you attach to your daily-driver profile/tab, you’re granting access to that account state. - -Recommendations: - -- Prefer a dedicated Chrome profile (separate from your personal browsing) for extension relay usage. -- Keep the Gateway and any node hosts tailnet-only; rely on Gateway auth + node pairing. -- Avoid exposing relay ports over LAN (`0.0.0.0`) and avoid Funnel (public). -- The relay blocks non-extension origins and requires gateway-token auth for both `/cdp` and `/extension`. - -Related: - -- Browser tool overview: [Browser](/tools/browser) -- Security audit: [Security](/gateway/security) -- Tailscale setup: [Tailscale](/gateway/tailscale) diff --git a/docs/tools/index.md b/docs/tools/index.md index dbca6cd26bf..deb42b0d76a 100644 --- a/docs/tools/index.md +++ b/docs/tools/index.md @@ -318,8 +318,7 @@ Common parameters: - All actions accept optional `profile` parameter for multi-instance support. - Omit `profile` for the safe default: isolated OpenClaw-managed browser (`openclaw`). - Use `profile="user"` for the real local host browser when existing logins/cookies matter and the user is present to click/approve any attach prompt. -- Use `profile="chrome-relay"` only for the Chrome extension / toolbar-button attach flow. -- `profile="user"` and `profile="chrome-relay"` are host-only; do not combine them with sandbox/node targets. +- `profile="user"` is host-only; do not combine it with sandbox/node targets. - When `profile` is omitted, uses `browser.defaultProfile` (defaults to `openclaw`). - Profile names: lowercase alphanumeric + hyphens only (max 64 chars). - Port range: 18800-18899 (~100 profiles max). diff --git a/docs/tools/plugin.md b/docs/tools/plugin.md index 32b9850a221..9269e8b1faf 100644 --- a/docs/tools/plugin.md +++ b/docs/tools/plugin.md @@ -218,9 +218,10 @@ Tool authoring guide: [Plugin agent tools](/plugins/agent-tools). Provider plugins now have two layers: - manifest metadata: `providerAuthEnvVars` for cheap env-auth lookup before - runtime load + runtime load, plus `providerAuthChoices` for cheap onboarding/auth-choice + labels and CLI flag metadata before runtime load - config-time hooks: `catalog` / legacy `discovery` -- runtime hooks: `resolveDynamicModel`, `prepareDynamicModel`, `normalizeResolvedModel`, `capabilities`, `prepareExtraParams`, `wrapStreamFn`, `isCacheTtlEligible`, `buildMissingAuthMessage`, `suppressBuiltInModel`, `augmentModelCatalog`, `isBinaryThinking`, `supportsXHighThinking`, `resolveDefaultThinkingLevel`, `isModernModelRef`, `prepareRuntimeAuth`, `resolveUsageAuth`, `fetchUsageSnapshot` +- runtime hooks: `resolveDynamicModel`, `prepareDynamicModel`, `normalizeResolvedModel`, `capabilities`, `prepareExtraParams`, `wrapStreamFn`, `formatApiKey`, `refreshOAuth`, `buildAuthDoctorHint`, `isCacheTtlEligible`, `buildMissingAuthMessage`, `suppressBuiltInModel`, `augmentModelCatalog`, `isBinaryThinking`, `supportsXHighThinking`, `resolveDefaultThinkingLevel`, `isModernModelRef`, `prepareRuntimeAuth`, `resolveUsageAuth`, `fetchUsageSnapshot` OpenClaw still owns the generic agent loop, failover, transcript handling, and tool policy. These hooks are the seam for provider-specific behavior without @@ -228,8 +229,11 @@ needing a whole custom inference transport. Use manifest `providerAuthEnvVars` when the provider has env-based credentials that generic auth/status/model-picker paths should see without loading plugin -runtime. Keep provider runtime `envVars` for operator-facing hints such as -onboarding labels or OAuth client-id/client-secret setup vars. +runtime. Use manifest `providerAuthChoices` when onboarding/auth-choice CLI +surfaces should know the provider's choice id, group labels, and simple +one-flag auth wiring without loading provider runtime. Keep provider runtime +`envVars` for operator-facing hints such as onboarding labels or OAuth +client-id/client-secret setup vars. ### Hook order @@ -254,31 +258,39 @@ For model/provider plugins, OpenClaw uses hooks in this rough order: Provider-owned request-param normalization before generic stream option wrappers. 8. `wrapStreamFn` Provider-owned stream wrapper after generic wrappers are applied. -9. `isCacheTtlEligible` - Provider-owned prompt-cache policy for proxy/backhaul providers. -10. `buildMissingAuthMessage` +9. `formatApiKey` + Provider-owned auth-profile formatter used when a stored auth profile needs + to become the runtime `apiKey` string. +10. `refreshOAuth` + Provider-owned OAuth refresh override for custom refresh endpoints or + refresh-failure policy. +11. `buildAuthDoctorHint` + Provider-owned repair hint appended when OAuth refresh fails. +12. `isCacheTtlEligible` + Provider-owned prompt-cache policy for proxy/backhaul providers. +13. `buildMissingAuthMessage` Provider-owned replacement for the generic missing-auth recovery message. -11. `suppressBuiltInModel` +14. `suppressBuiltInModel` Provider-owned stale upstream model suppression plus optional user-facing error hint. -12. `augmentModelCatalog` +15. `augmentModelCatalog` Provider-owned synthetic/final catalog rows appended after discovery. -13. `isBinaryThinking` +16. `isBinaryThinking` Provider-owned on/off reasoning toggle for binary-thinking providers. -14. `supportsXHighThinking` +17. `supportsXHighThinking` Provider-owned `xhigh` reasoning support for selected models. -15. `resolveDefaultThinkingLevel` +18. `resolveDefaultThinkingLevel` Provider-owned default `/think` level for a specific model family. -16. `isModernModelRef` +19. `isModernModelRef` Provider-owned modern-model matcher used by live profile filters and smoke selection. -17. `prepareRuntimeAuth` +20. `prepareRuntimeAuth` Exchanges a configured credential into the actual runtime token/key just before inference. -18. `resolveUsageAuth` +21. `resolveUsageAuth` Resolves usage/billing credentials for `/usage` and related status surfaces. -19. `fetchUsageSnapshot` +22. `fetchUsageSnapshot` Fetches and normalizes provider-specific usage/quota snapshots after auth is resolved. @@ -291,6 +303,9 @@ For model/provider plugins, OpenClaw uses hooks in this rough order: - `capabilities`: publish provider-family and transcript/tooling quirks without hardcoding provider ids in core - `prepareExtraParams`: set provider defaults or normalize provider-specific per-model params before generic stream wrapping - `wrapStreamFn`: add provider-specific headers/payload/model compat patches while still using the normal `pi-ai` execution path +- `formatApiKey`: turn a stored auth profile into the runtime `apiKey` string without hardcoding provider token blobs in core +- `refreshOAuth`: own OAuth refresh for providers that do not fit the shared `pi-ai` refreshers +- `buildAuthDoctorHint`: append provider-owned auth repair guidance when refresh fails - `isCacheTtlEligible`: decide whether provider/model pairs should use cache TTL metadata - `buildMissingAuthMessage`: replace the generic auth-store error with a provider-specific recovery hint - `suppressBuiltInModel`: hide stale upstream rows and optionally return a provider-owned error for direct resolution failures @@ -312,6 +327,9 @@ Rule of thumb: - provider needs transcript/provider-family quirks: use `capabilities` - provider needs default request params or per-provider param cleanup: use `prepareExtraParams` - provider needs request headers/body/model compat wrappers without a custom transport: use `wrapStreamFn` +- provider stores extra metadata in auth profiles and needs a custom runtime token shape: use `formatApiKey` +- provider needs a custom OAuth refresh endpoint or refresh failure policy: use `refreshOAuth` +- provider needs provider-owned auth repair guidance after refresh failure: use `buildAuthDoctorHint` - provider needs proxy-specific cache TTL gating: use `isCacheTtlEligible` - provider needs a provider-specific missing-auth recovery hint: use `buildMissingAuthMessage` - provider needs to hide stale upstream rows or replace them with a vendor hint: use `suppressBuiltInModel` @@ -384,11 +402,12 @@ api.registerProvider({ ### Built-in examples -- Anthropic uses `resolveDynamicModel`, `capabilities`, `resolveUsageAuth`, - `fetchUsageSnapshot`, `isCacheTtlEligible`, `resolveDefaultThinkingLevel`, - and `isModernModelRef` because it owns Claude 4.6 forward-compat, - provider-family hints, usage endpoint integration, prompt-cache - eligibility, and Claude default/adaptive thinking policy. +- Anthropic uses `resolveDynamicModel`, `capabilities`, `buildAuthDoctorHint`, + `resolveUsageAuth`, `fetchUsageSnapshot`, `isCacheTtlEligible`, + `resolveDefaultThinkingLevel`, and `isModernModelRef` because it owns Claude + 4.6 forward-compat, provider-family hints, auth repair guidance, usage + endpoint integration, prompt-cache eligibility, and Claude default/adaptive + thinking policy. - OpenAI uses `resolveDynamicModel`, `normalizeResolvedModel`, and `capabilities` plus `buildMissingAuthMessage`, `suppressBuiltInModel`, `augmentModelCatalog`, `supportsXHighThinking`, and `isModernModelRef` @@ -399,20 +418,22 @@ api.registerProvider({ - OpenRouter uses `catalog` plus `resolveDynamicModel` and `prepareDynamicModel` because the provider is pass-through and may expose new model ids before OpenClaw's static catalog updates. -- GitHub Copilot uses `catalog`, `resolveDynamicModel`, and +- GitHub Copilot uses `catalog`, `auth`, `resolveDynamicModel`, and `capabilities` plus `prepareRuntimeAuth` and `fetchUsageSnapshot` because it - needs model fallback behavior, Claude transcript quirks, a GitHub token -> - Copilot token exchange, and a provider-owned usage endpoint. + needs provider-owned device login, model fallback behavior, Claude transcript + quirks, a GitHub token -> Copilot token exchange, and a provider-owned usage + endpoint. - OpenAI Codex uses `catalog`, `resolveDynamicModel`, - `normalizeResolvedModel`, and `augmentModelCatalog` plus + `normalizeResolvedModel`, `refreshOAuth`, and `augmentModelCatalog` plus `prepareExtraParams`, `resolveUsageAuth`, and `fetchUsageSnapshot` because it still runs on core OpenAI transports but owns its transport/base URL - normalization, default transport choice, synthetic Codex catalog rows, and - ChatGPT usage endpoint integration. + normalization, OAuth refresh fallback policy, default transport choice, + synthetic Codex catalog rows, and ChatGPT usage endpoint integration. - Google AI Studio and Gemini CLI OAuth use `resolveDynamicModel` and `isModernModelRef` because they own Gemini 3.1 forward-compat fallback and - modern-model matching; Gemini CLI OAuth also uses `resolveUsageAuth` and - `fetchUsageSnapshot` for token parsing and quota endpoint wiring. + modern-model matching; Gemini CLI OAuth also uses `formatApiKey`, + `resolveUsageAuth`, and `fetchUsageSnapshot` for token formatting, token + parsing, and quota endpoint wiring. - OpenRouter uses `capabilities`, `wrapStreamFn`, and `isCacheTtlEligible` to keep provider-specific request headers, routing metadata, reasoning patches, and prompt-cache policy out of core. @@ -430,9 +451,10 @@ api.registerProvider({ - Mistral, OpenCode Zen, and OpenCode Go use `capabilities` only to keep transcript/tooling quirks out of core. - Catalog-only bundled providers such as `byteplus`, `cloudflare-ai-gateway`, - `huggingface`, `kimi-coding`, `minimax-portal`, `modelstudio`, `nvidia`, - `qianfan`, `qwen-portal`, `synthetic`, `together`, `venice`, - `vercel-ai-gateway`, and `volcengine` use `catalog` only. + `huggingface`, `kimi-coding`, `modelstudio`, `nvidia`, `qianfan`, + `synthetic`, `together`, `venice`, `vercel-ai-gateway`, and `volcengine` use + `catalog` only. +- Qwen portal uses `catalog`, `auth`, and `refreshOAuth`. - MiniMax and Xiaomi use `catalog` plus usage hooks because their `/usage` behavior is plugin-owned even though inference still runs through the shared transports. @@ -549,14 +571,14 @@ Notes: Use SDK subpaths instead of the monolithic `openclaw/plugin-sdk` import when authoring plugins: -- `openclaw/plugin-sdk/core` for generic plugin APIs, provider auth types, and shared helpers. +- `openclaw/plugin-sdk/core` for generic plugin APIs, provider auth types, and shared helpers such as routing/session utilities and logger-backed runtimes. - `openclaw/plugin-sdk/compat` for bundled/internal plugin code that needs broader shared runtime helpers than `core`. -- `openclaw/plugin-sdk/telegram` for Telegram channel plugins. -- `openclaw/plugin-sdk/discord` for Discord channel plugins. -- `openclaw/plugin-sdk/slack` for Slack channel plugins. -- `openclaw/plugin-sdk/signal` for Signal channel plugins. -- `openclaw/plugin-sdk/imessage` for iMessage channel plugins. -- `openclaw/plugin-sdk/whatsapp` for WhatsApp channel plugins. +- `openclaw/plugin-sdk/telegram` for Telegram channel plugin types and shared channel-facing helpers. Built-in Telegram implementation internals stay private to the bundled extension. +- `openclaw/plugin-sdk/discord` for Discord channel plugin types and shared channel-facing helpers. Built-in Discord implementation internals stay private to the bundled extension. +- `openclaw/plugin-sdk/slack` for Slack channel plugin types and shared channel-facing helpers. Built-in Slack implementation internals stay private to the bundled extension. +- `openclaw/plugin-sdk/signal` for Signal channel plugin types and shared channel-facing helpers. Built-in Signal implementation internals stay private to the bundled extension. +- `openclaw/plugin-sdk/imessage` for iMessage channel plugin types and shared channel-facing helpers. Built-in iMessage implementation internals stay private to the bundled extension. +- `openclaw/plugin-sdk/whatsapp` for WhatsApp channel plugin types and shared channel-facing helpers. Built-in WhatsApp implementation internals stay private to the bundled extension. - `openclaw/plugin-sdk/line` for LINE channel plugins. - `openclaw/plugin-sdk/msteams` for the bundled Microsoft Teams plugin surface. - Bundled extension-specific subpaths are also available: @@ -1177,11 +1199,11 @@ A provider plugin can participate in five distinct phases: `auth[].run(ctx)` performs OAuth, API-key capture, device code, or custom setup and returns auth profiles plus optional config patches. 2. **Non-interactive setup** - `auth[].runNonInteractive(ctx)` handles `openclaw setup --wizard --non-interactive` + `auth[].runNonInteractive(ctx)` handles `openclaw onboard --non-interactive` without prompts. Use this when the provider needs custom headless setup beyond the built-in simple API-key paths. 3. **Wizard integration** - `wizard.setup` adds an entry to `openclaw setup --wizard`. + `wizard.setup` adds an entry to `openclaw onboard`. `wizard.modelPicker` adds a setup entry to the model picker. 4. **Implicit discovery** `discovery.run(ctx)` can contribute provider config automatically during @@ -1248,6 +1270,16 @@ errors instead. ### Provider wizard metadata +Provider auth/onboarding metadata can live in two layers: + +- manifest `providerAuthChoices`: cheap labels, grouping, `--auth-choice` + ids, and simple CLI flag metadata available before runtime load +- runtime `wizard.setup` / `auth[].wizard`: richer behavior that depends on + loaded provider code + +Use manifest metadata for static labels/flags. Use runtime wizard metadata when +setup depends on dynamic auth methods, method fallback, or runtime validation. + `wizard.setup` controls how the provider appears in grouped onboarding: - `choiceId`: auth-choice value @@ -1257,6 +1289,7 @@ errors instead. - `groupLabel`: group label - `groupHint`: group hint - `methodId`: auth method to run +- `modelAllowlist`: optional post-auth allowlist policy (`allowedKeys`, `initialSelections`, `message`) `wizard.modelPicker` controls how a provider appears as a "set this up now" entry in model selection: @@ -1342,7 +1375,7 @@ or more auth methods (OAuth, API key, device code, etc.). Those methods can power: - `openclaw models auth login --provider [--method ]` -- `openclaw setup --wizard` +- `openclaw onboard` - model-picker “custom provider” setup entries - implicit provider discovery during model resolution/listing @@ -1417,8 +1450,13 @@ Notes: for headless onboarding. - Return `configPatch` when you need to add default models or provider config. - Return `defaultModel` so `--set-default` can update agent defaults. -- `wizard.setup` adds a provider choice to `openclaw setup --wizard`. +- `wizard.setup` adds a provider choice to onboarding surfaces such as + `openclaw onboard` / `openclaw setup --wizard`. +- `wizard.setup.modelAllowlist` lets the provider narrow the follow-up model + allowlist prompt during onboarding/configure. - `wizard.modelPicker` adds a “setup this provider” entry to the model picker. +- `deprecatedProfileIds` lets the provider own `openclaw doctor` cleanup for + retired auth-profile ids. - `discovery.run` returns either `{ provider }` for the plugin’s own provider id or `{ providers }` for multi-provider discovery. - `discovery.order` controls when the provider runs relative to built-in diff --git a/docs/zh-CN/automation/hooks.md b/docs/zh-CN/automation/hooks.md index b5806e2bdd0..b0615569eec 100644 --- a/docs/zh-CN/automation/hooks.md +++ b/docs/zh-CN/automation/hooks.md @@ -1,60 +1,61 @@ --- read_when: - - 你想为 /new、/reset、/stop 和智能体生命周期事件实现事件驱动自动化 - - 你想构建、安装或调试 hooks + - 你希望为 `/new`、`/reset`、`/stop` 和智能体生命周期事件使用事件驱动自动化 + - 你希望构建、安装或调试 Hooks summary: Hooks:用于命令和生命周期事件的事件驱动自动化 title: Hooks x-i18n: - generated_at: "2026-02-03T07:50:59Z" - model: claude-opus-4-5 - provider: pi - source_hash: 853227a0f1abd20790b425fa64dda60efc6b5f93c1b13ecd2dcb788268f71d79 + generated_at: "2026-03-16T06:21:34Z" + model: gpt-5.4 + provider: openai + source_hash: fc1370a05127d778eb685f687ee9a52062aa6f5c895e80152b9de41c3a02c592 source_path: automation/hooks.md workflow: 15 --- # Hooks -Hooks 提供了一个可扩展的事件驱动系统,用于响应智能体命令和事件自动执行操作。Hooks 从目录中自动发现,可以通过 CLI 命令管理,类似于 OpenClaw 中 Skills 的工作方式。 +Hooks 提供了一个可扩展的事件驱动系统,用于在响应智能体命令和事件时自动执行操作。Hooks 会从目录中自动发现,并且可以通过 CLI 命令进行管理,方式与 OpenClaw 中的 Skills 类似。 -## 入门指南 +## 熟悉基础 -Hooks 是在事件发生时运行的小脚本。有两种类型: +Hooks 是在某些事情发生时运行的小脚本。它们有两种类型: -- **Hooks**(本页):当智能体事件触发时在 Gateway 网关内运行,如 `/new`、`/reset`、`/stop` 或生命周期事件。 -- **Webhooks**:外部 HTTP webhooks,让其他系统触发 OpenClaw 中的工作。参见 [Webhook Hooks](/automation/webhook) 或使用 `openclaw webhooks` 获取 Gmail 助手命令。 +- **Hooks**(本页):当智能体事件触发时,在 Gateway 网关内运行,例如 `/new`、`/reset`、`/stop` 或生命周期事件。 +- **Webhooks**:外部 HTTP webhook,可让其他系统在 OpenClaw 中触发工作。请参阅 [Webhook Hooks](/automation/webhook),或使用 `openclaw webhooks` 获取 Gmail 辅助命令。 -Hooks 也可以捆绑在插件中;参见 [插件](/tools/plugin#plugin-hooks)。 +Hooks 也可以打包在插件中;请参阅 [Plugins](/tools/plugin#plugin-hooks)。 常见用途: -- 重置会话时保存记忆快照 -- 保留命令审计跟踪用于故障排除或合规 -- 会话开始或结束时触发后续自动化 -- 事件触发时向智能体工作区写入文件或调用外部 API +- 当你重置会话时保存一份内存快照 +- 为故障排除或合规保留命令审计轨迹 +- 当会话开始或结束时触发后续自动化 +- 当事件触发时,将文件写入智能体工作区或调用外部 API -如果你能写一个小的 TypeScript 函数,你就能写一个 hook。Hooks 会自动发现,你可以通过 CLI 启用或禁用它们。 +如果你会写一个小型 TypeScript 函数,你就能编写一个 hook。Hooks 会被自动发现,你可以通过 CLI 启用或禁用它们。 -## 概述 +## 概览 -hooks 系统允许你: +Hooks 系统允许你: -- 在发出 `/new` 时将会话上下文保存到记忆 +- 当发出 `/new` 时,将会话上下文保存到 memory - 记录所有命令以供审计 - 在智能体生命周期事件上触发自定义自动化 - 在不修改核心代码的情况下扩展 OpenClaw 的行为 -## 入门 +## 入门指南 -### 捆绑的 Hooks +### 内置 Hooks -OpenClaw 附带三个自动发现的捆绑 hooks: +OpenClaw 自带四个会被自动发现的内置 hook: -- **💾 session-memory**:当你发出 `/new` 时将会话上下文保存到智能体工作区(默认 `~/.openclaw/workspace/memory/`) +- **💾 session-memory**:当你发出 `/new` 时,将会话上下文保存到你的智能体工作区(默认是 `~/.openclaw/workspace/memory/`) +- **📎 bootstrap-extra-files**:在 `agent:bootstrap` 期间,从已配置的 glob/路径模式中注入额外的工作区引导文件 - **📝 command-logger**:将所有命令事件记录到 `~/.openclaw/logs/commands.log` - **🚀 boot-md**:当 Gateway 网关启动时运行 `BOOT.md`(需要启用内部 hooks) -列出可用的 hooks: +列出可用 hooks: ```bash openclaw hooks list @@ -80,35 +81,40 @@ openclaw hooks info session-memory ### 新手引导 -在新手引导期间(`openclaw onboard`),你将被提示启用推荐的 hooks。向导会自动发现符合条件的 hooks 并呈现供选择。 +在新手引导期间(`openclaw onboard`),系统会提示你启用推荐的 hooks。向导会自动发现符合条件的 hooks 并供你选择。 ## Hook 发现 -Hooks 从三个目录自动发现(按优先级顺序): +Hooks 会从三个目录中自动发现(按优先级顺序): -1. **工作区 hooks**:`/hooks/`(每智能体,最高优先级) -2. **托管 hooks**:`~/.openclaw/hooks/`(用户安装,跨工作区共享) -3. **捆绑 hooks**:`/dist/hooks/bundled/`(随 OpenClaw 附带) +1. **工作区 hooks**:`/hooks/`(每个智能体单独配置,优先级最高) +2. **托管 hooks**:`~/.openclaw/hooks/`(用户安装,在各工作区之间共享) +3. **内置 hooks**:`/dist/hooks/bundled/`(随 OpenClaw 一起提供) -托管 hook 目录可以是**单个 hook** 或 **hook 包**(包目录)。 +托管 hook 目录既可以是 **单个 hook**,也可以是 **hook 包**(包目录)。 -每个 hook 是一个包含以下内容的目录: +每个 hook 都是一个包含以下内容的目录: ``` my-hook/ ├── HOOK.md # 元数据 + 文档 -└── handler.ts # 处理程序实现 +└── handler.ts # 处理器实现 ``` -## Hook 包(npm/archives) +## Hook 包(npm/归档) -Hook 包是标准的 npm 包,通过 `package.json` 中的 `openclaw.hooks` 导出一个或多个 hooks。使用以下命令安装: +Hook 包是标准的 npm 包,它们通过 `package.json` 中的 `openclaw.hooks` 导出一个或多个 hook。使用以下命令安装它们: ```bash openclaw hooks install ``` -示例 `package.json`: +npm spec 仅支持注册表形式(包名 + 可选的精确版本或 dist-tag)。 +Git/URL/file spec 和 semver 范围会被拒绝。 + +裸 spec 和 `@latest` 会保持在稳定轨道上。如果 npm 将其中任意一种解析为预发布版本,OpenClaw 会停止并要求你通过预发布标签(例如 `@beta`/`@rc`)或精确的预发布版本显式选择加入。 + +`package.json` 示例: ```json { @@ -120,19 +126,23 @@ openclaw hooks install } ``` -每个条目指向包含 `HOOK.md` 和 `handler.ts`(或 `index.ts`)的 hook 目录。 -Hook 包可以附带依赖;它们将安装在 `~/.openclaw/hooks/` 下。 +每个条目都指向一个包含 `HOOK.md` 和 `handler.ts`(或 `index.ts`)的 hook 目录。 +Hook 包可以携带依赖;它们会安装到 `~/.openclaw/hooks/` 下。 +每个 `openclaw.hooks` 条目在解析符号链接后都必须保持在包目录内部;超出目录范围的条目会被拒绝。 + +安全说明:`openclaw hooks install` 会使用 `npm install --ignore-scripts` 安装依赖 +(不运行生命周期脚本)。请保持 hook 包依赖树为“纯 JS/TS”,并避免依赖 `postinstall` 构建的包。 ## Hook 结构 ### HOOK.md 格式 -`HOOK.md` 文件在 YAML frontmatter 中包含元数据,加上 Markdown 文档: +`HOOK.md` 文件包含 YAML frontmatter 中的元数据以及 Markdown 文档: ```markdown --- name: my-hook -description: "Short description of what this hook does" +description: "关于此 hook 功能的简短描述" homepage: https://docs.openclaw.ai/automation/hooks#my-hook metadata: { "openclaw": { "emoji": "🔗", "events": ["command:new"], "requires": { "bins": ["node"] } } } @@ -140,49 +150,47 @@ metadata: # My Hook -Detailed documentation goes here... +详细文档写在这里…… -## What It Does +## 它的作用 -- Listens for `/new` commands -- Performs some action -- Logs the result +- 监听 `/new` 命令 +- 执行某些操作 +- 记录结果 -## Requirements +## 要求 -- Node.js must be installed +- 必须安装 Node.js -## Configuration +## 配置 -No configuration needed. +无需配置。 ``` ### 元数据字段 `metadata.openclaw` 对象支持: -- **`emoji`**:CLI 的显示表情符号(例如 `"💾"`) +- **`emoji`**:CLI 显示用 emoji(例如 `"💾"`) - **`events`**:要监听的事件数组(例如 `["command:new", "command:reset"]`) - **`export`**:要使用的命名导出(默认为 `"default"`) - **`homepage`**:文档 URL - **`requires`**:可选要求 - - **`bins`**:PATH 中需要的二进制文件(例如 `["git", "node"]`) - - **`anyBins`**:这些二进制文件中至少有一个必须存在 - - **`env`**:需要的环境变量 - - **`config`**:需要的配置路径(例如 `["workspace.dir"]`) - - **`os`**:需要的平台(例如 `["darwin", "linux"]`) + - **`bins`**:PATH 中必须存在的二进制文件(例如 `["git", "node"]`) + - **`anyBins`**:这些二进制文件中至少要存在一个 + - **`env`**:必需的环境变量 + - **`config`**:必需的配置路径(例如 `["workspace.dir"]`) + - **`os`**:支持的平台(例如 `["darwin", "linux"]`) - **`always`**:绕过资格检查(布尔值) -- **`install`**:安装方法(对于捆绑 hooks:`[{"id":"bundled","kind":"bundled"}]`) +- **`install`**:安装方式(对于内置 hooks:`[{"id":"bundled","kind":"bundled"}]`) -### 处理程序实现 +### 处理器实现 -`handler.ts` 文件导出一个 `HookHandler` 函数: +`handler.ts` 文件会导出一个 `HookHandler` 函数: ```typescript -import type { HookHandler } from "../../src/hooks/hooks.js"; - -const myHandler: HookHandler = async (event) => { - // Only trigger on 'new' command +const myHandler = async (event) => { + // 仅在 'new' 命令时触发 if (event.type !== "command" || event.action !== "new") { return; } @@ -191,9 +199,9 @@ const myHandler: HookHandler = async (event) => { console.log(` Session: ${event.sessionKey}`); console.log(` Timestamp: ${event.timestamp.toISOString()}`); - // Your custom logic here + // 你的自定义逻辑写在这里 - // Optionally send message to user + // 可选:向用户发送消息 event.messages.push("✨ My hook executed!"); }; @@ -202,24 +210,31 @@ export default myHandler; #### 事件上下文 -每个事件包含: +每个事件都包含: ```typescript { - type: 'command' | 'session' | 'agent' | 'gateway', - action: string, // e.g., 'new', 'reset', 'stop' - sessionKey: string, // Session identifier - timestamp: Date, // When the event occurred - messages: string[], // Push messages here to send to user + type: 'command' | 'session' | 'agent' | 'gateway' | 'message', + action: string, // 例如 'new'、'reset'、'stop'、'received'、'sent' + sessionKey: string, // 会话标识符 + timestamp: Date, // 事件发生时间 + messages: string[], // 将消息推入这里以发送给用户 context: { + // 命令事件: sessionEntry?: SessionEntry, sessionId?: string, sessionFile?: string, - commandSource?: string, // e.g., 'whatsapp', 'telegram' + commandSource?: string, // 例如 'whatsapp'、'telegram' senderId?: string, workspaceDir?: string, bootstrapFiles?: WorkspaceBootstrapFile[], - cfg?: OpenClawConfig + cfg?: OpenClawConfig, + // 消息事件(完整详情见“消息事件”部分): + from?: string, // message:received + to?: string, // message:sent + content?: string, + channelId?: string, + success?: boolean, // message:sent } } ``` @@ -228,28 +243,135 @@ export default myHandler; ### 命令事件 -当发出智能体命令时触发: +在发出智能体命令时触发: - **`command`**:所有命令事件(通用监听器) -- **`command:new`**:当发出 `/new` 命令时 -- **`command:reset`**:当发出 `/reset` 命令时 -- **`command:stop`**:当发出 `/stop` 命令时 +- **`command:new`**:发出 `/new` 命令时 +- **`command:reset`**:发出 `/reset` 命令时 +- **`command:stop`**:发出 `/stop` 命令时 + +### 会话事件 + +- **`session:compact:before`**:在压缩开始总结历史记录之前 +- **`session:compact:after`**:在压缩完成并带有摘要元数据之后 + +内部 hook 负载会将这些事件表示为 `type: "session"`,并将 `action` 设为 `"compact:before"` / `"compact:after"`;监听器使用上面的组合键进行订阅。 +具体处理器注册使用字面量键格式 `${type}:${action}`。对于这些事件,请注册 `session:compact:before` 和 `session:compact:after`。 ### 智能体事件 -- **`agent:bootstrap`**:在注入工作区引导文件之前(hooks 可以修改 `context.bootstrapFiles`) +- **`agent:bootstrap`**:在工作区引导文件被注入之前(hooks 可以修改 `context.bootstrapFiles`) ### Gateway 网关事件 -当 Gateway 网关启动时触发: +在 Gateway 网关启动时触发: -- **`gateway:startup`**:在渠道启动和 hooks 加载之后 +- **`gateway:startup`**:在渠道启动且 hooks 已加载之后 + +### 消息事件 + +在消息被接收或发送时触发: + +- **`message`**:所有消息事件(通用监听器) +- **`message:received`**:当从任意渠道收到入站消息时。在处理的早期阶段触发,此时媒体理解尚未完成。对于尚未处理的媒体附件,内容中可能包含类似 `` 的原始占位符。 +- **`message:transcribed`**:当一条消息已被完全处理,包括音频转写和链接理解时触发。此时,`transcript` 包含音频消息的完整转写文本。当你需要访问已转写的音频内容时,请使用此 hook。 +- **`message:preprocessed`**:在所有媒体 + 链接理解完成后,为每条消息触发,使 hooks 可以在智能体看到消息之前访问完全增强的正文(转写、图像描述、链接摘要)。 +- **`message:sent`**:当出站消息成功发送时 + +#### 消息事件上下文 + +消息事件包含关于消息的丰富上下文: + +```typescript +// message:received context +{ + from: string, // 发送者标识符(电话号码、用户 ID 等) + content: string, // 消息内容 + timestamp?: number, // 接收时的 Unix 时间戳 + channelId: string, // 渠道(例如 "whatsapp"、"telegram"、"discord") + accountId?: string, // 多账号设置中的提供商账号 ID + conversationId?: string, // 聊天/会话 ID + messageId?: string, // 提供商返回的消息 ID + metadata?: { // 额外的提供商特定数据 + to?: string, + provider?: string, + surface?: string, + threadId?: string, + senderId?: string, + senderName?: string, + senderUsername?: string, + senderE164?: string, + } +} + +// message:sent context +{ + to: string, // 接收者标识符 + content: string, // 已发送的消息内容 + success: boolean, // 发送是否成功 + error?: string, // 如果发送失败,则为错误消息 + channelId: string, // 渠道(例如 "whatsapp"、"telegram"、"discord") + accountId?: string, // 提供商账号 ID + conversationId?: string, // 聊天/会话 ID + messageId?: string, // 提供商返回的消息 ID + isGroup?: boolean, // 此出站消息是否属于群组/渠道上下文 + groupId?: string, // 用于与 message:received 关联的群组/渠道标识符 +} + +// message:transcribed context +{ + body?: string, // 增强前的原始入站正文 + bodyForAgent?: string, // 对智能体可见的增强正文 + transcript: string, // 音频转写文本 + channelId: string, // 渠道(例如 "telegram"、"whatsapp") + conversationId?: string, + messageId?: string, +} + +// message:preprocessed context +{ + body?: string, // 原始入站正文 + bodyForAgent?: string, // 媒体/链接理解后的最终增强正文 + transcript?: string, // 存在音频时的转写内容 + channelId: string, // 渠道(例如 "telegram"、"whatsapp") + conversationId?: string, + messageId?: string, + isGroup?: boolean, + groupId?: string, +} +``` + +#### 示例:消息记录器 Hook + +```typescript +const isMessageReceivedEvent = (event: { type: string; action: string }) => + event.type === "message" && event.action === "received"; +const isMessageSentEvent = (event: { type: string; action: string }) => + event.type === "message" && event.action === "sent"; + +const handler = async (event) => { + if (isMessageReceivedEvent(event as { type: string; action: string })) { + console.log(`[message-logger] Received from ${event.context.from}: ${event.context.content}`); + } else if (isMessageSentEvent(event as { type: string; action: string })) { + console.log(`[message-logger] Sent to ${event.context.to}: ${event.context.content}`); + } +}; + +export default handler; +``` ### 工具结果 Hooks(插件 API) -这些 hooks 不是事件流监听器;它们让插件在 OpenClaw 持久化工具结果之前同步调整它们。 +这些 hooks 不是事件流监听器;它们允许插件在 OpenClaw 持久化工具结果之前同步调整工具结果。 -- **`tool_result_persist`**:在工具结果写入会话记录之前转换它们。必须是同步的;返回更新后的工具结果负载或 `undefined` 保持原样。参见 [智能体循环](/concepts/agent-loop)。 +- **`tool_result_persist`**:在工具结果写入会话转录之前对其进行转换。必须是同步的;返回更新后的工具结果负载,或返回 `undefined` 以保持原样。请参阅 [Agent Loop](/concepts/agent-loop)。 + +### 插件 Hook 事件 + +通过插件 hook 运行器公开的压缩生命周期 hooks: + +- **`before_compaction`**:在压缩前运行,并带有计数/token 元数据 +- **`after_compaction`**:在压缩后运行,并带有压缩摘要元数据 ### 未来事件 @@ -258,14 +380,12 @@ export default myHandler; - **`session:start`**:当新会话开始时 - **`session:end`**:当会话结束时 - **`agent:error`**:当智能体遇到错误时 -- **`message:sent`**:当消息被发送时 -- **`message:received`**:当消息被接收时 ## 创建自定义 Hooks ### 1. 选择位置 -- **工作区 hooks**(`/hooks/`):每智能体,最高优先级 +- **工作区 hooks**(`/hooks/`):每个智能体单独配置,优先级最高 - **托管 hooks**(`~/.openclaw/hooks/`):跨工作区共享 ### 2. 创建目录结构 @@ -280,27 +400,25 @@ cd ~/.openclaw/hooks/my-hook ```markdown --- name: my-hook -description: "Does something useful" +description: "执行某些有用的事情" metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } } --- # My Custom Hook -This hook does something useful when you issue `/new`. +当你发出 `/new` 时,此 hook 会执行一些有用的事情。 ``` ### 4. 创建 handler.ts ```typescript -import type { HookHandler } from "../../src/hooks/hooks.js"; - -const handler: HookHandler = async (event) => { +const handler = async (event) => { if (event.type !== "command" || event.action !== "new") { return; } console.log("[my-hook] Running!"); - // Your logic here + // 你的逻辑写在这里 }; export default handler; @@ -309,16 +427,16 @@ export default handler; ### 5. 启用并测试 ```bash -# Verify hook is discovered +# 验证 hook 已被发现 openclaw hooks list -# Enable it +# 启用它 openclaw hooks enable my-hook -# Restart your gateway process (menu bar app restart on macOS, or restart your dev process) +# 重启你的 Gateway 网关进程(macOS 上重启菜单栏应用,或重启你的开发进程) -# Trigger the event -# Send /new via your messaging channel +# 触发事件 +# 通过你的消息渠道发送 /new ``` ## 配置 @@ -339,9 +457,9 @@ openclaw hooks enable my-hook } ``` -### 每 Hook 配置 +### 每个 Hook 的配置 -Hooks 可以有自定义配置: +Hooks 可以具有自定义配置: ```json { @@ -378,9 +496,9 @@ Hooks 可以有自定义配置: } ``` -### 遗留配置格式(仍然支持) +### 旧版配置格式(仍受支持) -旧配置格式仍然有效以保持向后兼容: +旧配置格式仍可用于向后兼容: ```json { @@ -399,74 +517,76 @@ Hooks 可以有自定义配置: } ``` -**迁移**:对新 hooks 使用基于发现的新系统。遗留处理程序在基于目录的 hooks 之后加载。 +注意:`module` 必须是相对于工作区的路径。绝对路径和超出工作区范围的遍历路径会被拒绝。 + +**迁移**:对于新的 hooks,请使用基于发现的新系统。旧版 handlers 会在基于目录的 hooks 之后加载。 ## CLI 命令 ### 列出 Hooks ```bash -# List all hooks +# 列出所有 hooks openclaw hooks list -# Show only eligible hooks +# 仅显示符合条件的 hooks openclaw hooks list --eligible -# Verbose output (show missing requirements) +# 详细输出(显示缺失的要求) openclaw hooks list --verbose -# JSON output +# JSON 输出 openclaw hooks list --json ``` ### Hook 信息 ```bash -# Show detailed info about a hook +# 显示某个 hook 的详细信息 openclaw hooks info session-memory -# JSON output +# JSON 输出 openclaw hooks info session-memory --json ``` ### 检查资格 ```bash -# Show eligibility summary +# 显示资格摘要 openclaw hooks check -# JSON output +# JSON 输出 openclaw hooks check --json ``` ### 启用/禁用 ```bash -# Enable a hook +# 启用一个 hook openclaw hooks enable session-memory -# Disable a hook +# 禁用一个 hook openclaw hooks disable command-logger ``` -## 捆绑的 Hooks +## 内置 hook 参考 ### session-memory -当你发出 `/new` 时将会话上下文保存到记忆。 +当你发出 `/new` 时,将会话上下文保存到 memory。 **事件**:`command:new` **要求**:必须配置 `workspace.dir` -**输出**:`/memory/YYYY-MM-DD-slug.md`(默认为 `~/.openclaw/workspace`) +**输出**:`/memory/YYYY-MM-DD-slug.md`(默认是 `~/.openclaw/workspace`) -**功能**: +**它的作用**: -1. 使用预重置会话条目定位正确的记录 -2. 提取最后 15 行对话 -3. 使用 LLM 生成描述性文件名 slug -4. 将会话元数据保存到带日期的记忆文件 +1. 使用重置前的会话条目定位正确的转录 +2. 提取最近 15 行对话 +3. 使用 LLM 生成描述性的文件名 slug +4. 将会话元数据保存到带日期的 memory 文件中 **示例输出**: @@ -482,7 +602,7 @@ openclaw hooks disable command-logger - `2026-01-16-vendor-pitch.md` - `2026-01-16-api-design.md` -- `2026-01-16-1430.md`(如果 slug 生成失败则回退到时间戳) +- `2026-01-16-1430.md`(如果 slug 生成失败,则回退为时间戳) **启用**: @@ -490,9 +610,50 @@ openclaw hooks disable command-logger openclaw hooks enable session-memory ``` +### bootstrap-extra-files + +在 `agent:bootstrap` 期间注入额外的引导文件(例如 monorepo 本地的 `AGENTS.md` / `TOOLS.md`)。 + +**事件**:`agent:bootstrap` + +**要求**:必须配置 `workspace.dir` + +**输出**:不写入文件;仅在内存中修改引导上下文。 + +**配置**: + +```json +{ + "hooks": { + "internal": { + "enabled": true, + "entries": { + "bootstrap-extra-files": { + "enabled": true, + "paths": ["packages/*/AGENTS.md", "packages/*/TOOLS.md"] + } + } + } + } +} +``` + +**说明**: + +- 路径相对于工作区解析。 +- 文件必须保持在工作区内部(通过 realpath 检查)。 +- 仅加载已识别的引导基础文件名。 +- 会保留子智能体允许列表(仅 `AGENTS.md` 和 `TOOLS.md`)。 + +**启用**: + +```bash +openclaw hooks enable bootstrap-extra-files +``` + ### command-logger -将所有命令事件记录到集中审计文件。 +将所有命令事件记录到集中式审计文件。 **事件**:`command` @@ -500,10 +661,10 @@ openclaw hooks enable session-memory **输出**:`~/.openclaw/logs/commands.log` -**功能**: +**它的作用**: 1. 捕获事件详情(命令操作、时间戳、会话键、发送者 ID、来源) -2. 以 JSONL 格式追加到日志文件 +2. 以 JSONL 格式附加到日志文件 3. 在后台静默运行 **示例日志条目**: @@ -516,13 +677,13 @@ openclaw hooks enable session-memory **查看日志**: ```bash -# View recent commands +# 查看最近的命令 tail -n 20 ~/.openclaw/logs/commands.log -# Pretty-print with jq +# 使用 jq 美化输出 cat ~/.openclaw/logs/commands.log | jq . -# Filter by action +# 按操作筛选 grep '"action":"new"' ~/.openclaw/logs/commands.log | jq . ``` @@ -534,18 +695,18 @@ openclaw hooks enable command-logger ### boot-md -当 Gateway 网关启动时运行 `BOOT.md`(在渠道启动之后)。 -必须启用内部 hooks 才能运行。 +当 Gateway 网关启动时(渠道启动之后)运行 `BOOT.md`。 +必须启用内部 hooks,此功能才会运行。 **事件**:`gateway:startup` **要求**:必须配置 `workspace.dir` -**功能**: +**它的作用**: 1. 从你的工作区读取 `BOOT.md` -2. 通过智能体运行器运行指令 -3. 通过 message 工具发送任何请求的出站消息 +2. 通过智能体运行器执行其中的指令 +3. 通过消息工具发送任何请求的出站消息 **启用**: @@ -555,26 +716,26 @@ openclaw hooks enable boot-md ## 最佳实践 -### 保持处理程序快速 +### 保持处理器快速 -Hooks 在命令处理期间运行。保持它们轻量: +Hooks 在命令处理期间运行。请保持其轻量: ```typescript -// ✓ Good - async work, returns immediately +// ✓ 好 - 异步工作,立即返回 const handler: HookHandler = async (event) => { - void processInBackground(event); // Fire and forget + void processInBackground(event); // 触发后不等待 }; -// ✗ Bad - blocks command processing +// ✗ 差 - 阻塞命令处理 const handler: HookHandler = async (event) => { await slowDatabaseQuery(event); await evenSlowerAPICall(event); }; ``` -### 优雅处理错误 +### 优雅地处理错误 -始终包装有风险的操作: +始终包装高风险操作: ```typescript const handler: HookHandler = async (event) => { @@ -582,112 +743,117 @@ const handler: HookHandler = async (event) => { await riskyOperation(event); } catch (err) { console.error("[my-handler] Failed:", err instanceof Error ? err.message : String(err)); - // Don't throw - let other handlers run + // 不要抛出错误 - 让其他处理器继续运行 } }; ``` ### 尽早过滤事件 -如果事件不相关则尽早返回: +如果事件不相关,请尽早返回: ```typescript const handler: HookHandler = async (event) => { - // Only handle 'new' commands + // 仅处理 'new' 命令 if (event.type !== "command" || event.action !== "new") { return; } - // Your logic here + // 你的逻辑写在这里 }; ``` -### 使用特定事件键 +### 使用具体事件键 -尽可能在元数据中指定确切事件: +如果可能,请在元数据中指定精确事件: ```yaml -metadata: { "openclaw": { "events": ["command:new"] } } # Specific +metadata: { "openclaw": { "events": ["command:new"] } } # 精确 ``` 而不是: ```yaml -metadata: { "openclaw": { "events": ["command"] } } # General - more overhead +metadata: { "openclaw": { "events": ["command"] } } # 通用 - 开销更大 ``` ## 调试 ### 启用 Hook 日志 -Gateway 网关在启动时记录 hook 加载: +Gateway 网关会在启动时记录 hook 加载情况: ``` Registered hook: session-memory -> command:new +Registered hook: bootstrap-extra-files -> agent:bootstrap Registered hook: command-logger -> command Registered hook: boot-md -> gateway:startup ``` -### 检查发现 +### 检查发现情况 -列出所有发现的 hooks: +列出所有已发现的 hooks: ```bash openclaw hooks list --verbose ``` -### 检查注册 +### 检查注册情况 -在你的处理程序中,记录它被调用的时间: +在你的处理器中,记录它何时被调用: ```typescript const handler: HookHandler = async (event) => { console.log("[my-handler] Triggered:", event.type, event.action); - // Your logic + // 你的逻辑 }; ``` ### 验证资格 -检查为什么 hook 不符合条件: +检查某个 hook 为什么不符合条件: ```bash openclaw hooks info my-hook ``` -在输出中查找缺失的要求。 +查看输出中缺失的要求。 ## 测试 ### Gateway 网关日志 -监控 Gateway 网关日志以查看 hook 执行: +监控 Gateway 网关日志以查看 hook 执行情况: ```bash # macOS ./scripts/clawlog.sh -f -# Other platforms +# 其他平台 tail -f ~/.openclaw/gateway.log ``` ### 直接测试 Hooks -隔离测试你的处理程序: +单独测试你的 handlers: ```typescript import { test } from "vitest"; -import { createHookEvent } from "./src/hooks/hooks.js"; import myHandler from "./hooks/my-hook/handler.js"; test("my handler works", async () => { - const event = createHookEvent("command", "new", "test-session", { - foo: "bar", - }); + const event = { + type: "command", + action: "new", + sessionKey: "test-session", + timestamp: new Date(), + messages: [], + context: { foo: "bar" }, + }; await myHandler(event); - // Assert side effects + // 断言副作用 }); ``` @@ -696,8 +862,8 @@ test("my handler works", async () => { ### 核心组件 - **`src/hooks/types.ts`**:类型定义 -- **`src/hooks/workspace.ts`**:目录扫描和加载 -- **`src/hooks/frontmatter.ts`**:HOOK.md 元数据解析 +- **`src/hooks/workspace.ts`**:目录扫描与加载 +- **`src/hooks/frontmatter.ts`**:`HOOK.md` 元数据解析 - **`src/hooks/config.ts`**:资格检查 - **`src/hooks/hooks-status.ts`**:状态报告 - **`src/hooks/loader.ts`**:动态模块加载器 @@ -710,15 +876,15 @@ test("my handler works", async () => { ``` Gateway 网关启动 ↓ -扫描目录(工作区 → 托管 → 捆绑) +扫描目录(工作区 → 托管 → 内置) ↓ 解析 HOOK.md 文件 ↓ 检查资格(bins、env、config、os) ↓ -从符合条件的 hooks 加载处理程序 +从符合条件的 hooks 加载 handlers ↓ -为事件注册处理程序 +为事件注册 handlers ``` ### 事件流程 @@ -726,11 +892,11 @@ Gateway 网关启动 ``` 用户发送 /new ↓ -命令验证 +命令校验 ↓ 创建 hook 事件 ↓ -触发 hook(所有注册的处理程序) +触发 hook(所有已注册的 handlers) ↓ 命令处理继续 ↓ @@ -745,17 +911,18 @@ Gateway 网关启动 ```bash ls -la ~/.openclaw/hooks/my-hook/ - # Should show: HOOK.md, handler.ts + # 应显示:HOOK.md, handler.ts ``` 2. 验证 HOOK.md 格式: ```bash cat ~/.openclaw/hooks/my-hook/HOOK.md - # Should have YAML frontmatter with name and metadata + # 应包含带有 name 和 metadata 的 YAML frontmatter ``` -3. 列出所有发现的 hooks: +3. 列出所有已发现的 hooks: + ```bash openclaw hooks list ``` @@ -768,12 +935,12 @@ Gateway 网关启动 openclaw hooks info my-hook ``` -查找缺失的: +查看是否缺少: - 二进制文件(检查 PATH) - 环境变量 - 配置值 -- 操作系统兼容性 +- OS 兼容性 ### Hook 未执行 @@ -781,30 +948,31 @@ openclaw hooks info my-hook ```bash openclaw hooks list - # Should show ✓ next to enabled hooks + # 应在已启用的 hooks 旁显示 ✓ ``` 2. 重启你的 Gateway 网关进程以重新加载 hooks。 3. 检查 Gateway 网关日志中的错误: + ```bash ./scripts/clawlog.sh | grep hook ``` -### 处理程序错误 +### 处理器错误 检查 TypeScript/import 错误: ```bash -# Test import directly +# 直接测试导入 node -e "import('./path/to/handler.ts').then(console.log)" ``` ## 迁移指南 -### 从遗留配置到发现 +### 从旧版配置迁移到发现机制 -**之前**: +**迁移前**: ```json { @@ -822,7 +990,7 @@ node -e "import('./path/to/handler.ts').then(console.log)" } ``` -**之后**: +**迁移后**: 1. 创建 hook 目录: @@ -836,13 +1004,13 @@ node -e "import('./path/to/handler.ts').then(console.log)" ```markdown --- name: my-hook - description: "My custom hook" + description: "我的自定义 hook" metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } } --- # My Hook - Does something useful. + 执行某些有用的事情。 ``` 3. 更新配置: @@ -861,9 +1029,10 @@ node -e "import('./path/to/handler.ts').then(console.log)" ``` 4. 验证并重启你的 Gateway 网关进程: + ```bash openclaw hooks list - # Should show: 🎯 my-hook ✓ + # 应显示:🎯 my-hook ✓ ``` **迁移的好处**: @@ -876,7 +1045,7 @@ node -e "import('./path/to/handler.ts').then(console.log)" ## 另请参阅 -- [CLI 参考:hooks](/cli/hooks) -- [捆绑 Hooks README](https://github.com/openclaw/openclaw/tree/main/src/hooks/bundled) +- [CLI Reference: hooks](/cli/hooks) +- [Bundled Hooks README](https://github.com/openclaw/openclaw/tree/main/src/hooks/bundled) - [Webhook Hooks](/automation/webhook) -- [配置](/gateway/configuration#hooks) +- [Configuration](/gateway/configuration#hooks) diff --git a/docs/zh-CN/channels/bluebubbles.md b/docs/zh-CN/channels/bluebubbles.md index 4ee4cb71e3e..b7f8534065b 100644 --- a/docs/zh-CN/channels/bluebubbles.md +++ b/docs/zh-CN/channels/bluebubbles.md @@ -6,34 +6,35 @@ read_when: summary: 通过 BlueBubbles macOS 服务器使用 iMessage(REST 发送/接收、输入状态、回应、配对、高级操作)。 title: BlueBubbles x-i18n: - generated_at: "2026-02-03T10:04:52Z" - model: claude-opus-4-5 - provider: pi - source_hash: 3aae277a8bec479800a7f6268bfbca912c65a4aadc6e513694057fb873597b69 + generated_at: "2026-03-16T06:21:08Z" + model: gpt-5.4 + provider: openai + source_hash: 877592bf7b9b06abdddd7567d56e756eff229d6ffa5056ef33fa3356086aa580 source_path: channels/bluebubbles.md workflow: 15 --- # BlueBubbles(macOS REST) -状态:内置插件,通过 HTTP 与 BlueBubbles macOS 服务器通信。由于其更丰富的 API 和更简便的设置,**推荐用于 iMessage 集成**,优于旧版 imsg 渠道。 +状态:内置插件,通过 HTTP 与 BlueBubbles macOS 服务器通信。由于其 API 更丰富且设置比旧版 imsg 渠道更简单,**推荐用于 iMessage 集成**。 -## 概述 +## 概览 - 通过 BlueBubbles 辅助应用在 macOS 上运行([bluebubbles.app](https://bluebubbles.app))。 -- 推荐/已测试版本:macOS Sequoia (15)。macOS Tahoe (26) 可用;但在 Tahoe 上编辑功能目前不可用,群组图标更新可能显示成功但实际未同步。 -- OpenClaw 通过其 REST API 与之通信(`GET /api/v1/ping`、`POST /message/text`、`POST /chat/:id/*`)。 -- 传入消息通过 webhook 到达;发出的回复、输入指示器、已读回执和 tapback 均为 REST 调用。 -- 附件和贴纸作为入站媒体被接收(并在可能时呈现给智能体)。 -- 配对/白名单的工作方式与其他渠道相同(`/channels/pairing` 等),使用 `channels.bluebubbles.allowFrom` + 配对码。 -- 回应作为系统事件呈现,与 Slack/Telegram 类似,智能体可以在回复前"提及"它们。 +- 推荐/已测试:macOS Sequoia(15)。macOS Tahoe(26)可用;目前编辑功能在 Tahoe 上已损坏,群组图标更新可能会报告成功但不会同步。 +- OpenClaw 通过其 REST API 与其通信(`GET /api/v1/ping`、`POST /message/text`、`POST /chat/:id/*`)。 +- 传入消息通过 webhook 到达;传出回复、输入状态指示、已读回执和 tapback 都通过 REST 调用完成。 +- 附件和贴纸会作为入站媒体接收(并在可能时展示给智能体)。 +- 配对/allowlist 的工作方式与其他渠道相同(`/channels/pairing` 等),使用 `channels.bluebubbles.allowFrom` + 配对码。 +- 回应会像 Slack/Telegram 一样作为系统事件呈现,因此智能体可以在回复前“提及”它们。 - 高级功能:编辑、撤回、回复线程、消息效果、群组管理。 ## 快速开始 -1. 在你的 Mac 上安装 BlueBubbles 服务器(按照 [bluebubbles.app/install](https://bluebubbles.app/install) 的说明操作)。 -2. 在 BlueBubbles 配置中,启用 web API 并设置密码。 +1. 在你的 Mac 上安装 BlueBubbles 服务器(按照 [bluebubbles.app/install](https://bluebubbles.app/install) 上的说明操作)。 +2. 在 BlueBubbles 配置中启用 Web API 并设置密码。 3. 运行 `openclaw onboard` 并选择 BlueBubbles,或手动配置: + ```json5 { channels: { @@ -46,8 +47,89 @@ x-i18n: }, } ``` + 4. 将 BlueBubbles webhook 指向你的 Gateway 网关(示例:`https://your-gateway-host:3000/bluebubbles-webhook?password=`)。 -5. 启动 Gateway 网关;它将注册 webhook 处理程序并开始配对。 +5. 启动 Gateway 网关;它会注册 webhook 处理器并开始配对。 + +安全说明: + +- 始终设置 webhook 密码。 +- 始终要求进行 webhook 身份验证。无论 loopback/代理拓扑如何,除非 BlueBubbles webhook 请求包含与 `channels.bluebubbles.password` 匹配的 password/guid(例如 `?password=` 或 `x-password`),否则 OpenClaw 会拒绝该请求。 +- 在读取/解析完整 webhook 请求体之前就会检查密码身份验证。 + +## 保持 Messages.app 处于活动状态(VM / 无头设置) + +某些 macOS VM / 常开设置可能会导致 Messages.app 进入“空闲”状态(传入事件会停止,直到应用被打开/切到前台)。一个简单的解决方法是使用 AppleScript + LaunchAgent **每 5 分钟“触碰”一次 Messages**。 + +### 1)保存 AppleScript + +将以下内容保存为: + +- `~/Scripts/poke-messages.scpt` + +示例脚本(非交互式;不会抢占焦点): + +```applescript +try + tell application "Messages" + if not running then + launch + end if + + -- Touch the scripting interface to keep the process responsive. + set _chatCount to (count of chats) + end tell +on error + -- Ignore transient failures (first-run prompts, locked session, etc). +end try +``` + +### 2)安装 LaunchAgent + +将以下内容保存为: + +- `~/Library/LaunchAgents/com.user.poke-messages.plist` + +```xml + + + + + Label + com.user.poke-messages + + ProgramArguments + + /bin/bash + -lc + /usr/bin/osascript "$HOME/Scripts/poke-messages.scpt" + + + RunAtLoad + + + StartInterval + 300 + + StandardOutPath + /tmp/poke-messages.log + StandardErrorPath + /tmp/poke-messages.err + + +``` + +说明: + +- 这会**每 300 秒**运行一次,并且**在登录时**运行。 +- 首次运行可能会触发 macOS 的**自动化**权限提示(`osascript` → Messages)。请在运行该 LaunchAgent 的同一用户会话中批准这些提示。 + +加载它: + +```bash +launchctl unload ~/Library/LaunchAgents/com.user.poke-messages.plist 2>/dev/null || true +launchctl load ~/Library/LaunchAgents/com.user.poke-messages.plist +``` ## 新手引导 @@ -60,10 +142,10 @@ openclaw onboard 向导会提示输入: - **服务器 URL**(必填):BlueBubbles 服务器地址(例如 `http://192.168.1.100:1234`) -- **密码**(必填):来自 BlueBubbles 服务器设置的 API 密码 +- **密码**(必填):来自 BlueBubbles Server 设置的 API 密码 - **Webhook 路径**(可选):默认为 `/bluebubbles-webhook` -- **私信策略**:配对、白名单、开放或禁用 -- **白名单**:电话号码、电子邮件或聊天目标 +- **私信策略**:pairing、allowlist、open 或 disabled +- **允许列表**:电话号码、电子邮件或聊天目标 你也可以通过 CLI 添加 BlueBubbles: @@ -75,12 +157,12 @@ openclaw channels add bluebubbles --http-url http://192.168.1.100:1234 --passwor 私信: -- 默认:`channels.bluebubbles.dmPolicy = "pairing"`。 -- 未知发送者会收到配对码;在批准之前消息会被忽略(配对码 1 小时后过期)。 -- 批准方式: +- 默认值:`channels.bluebubbles.dmPolicy = "pairing"`。 +- 未知发送者会收到一个配对码;在获得批准前,消息会被忽略(代码 1 小时后过期)。 +- 通过以下方式批准: - `openclaw pairing list bluebubbles` - `openclaw pairing approve bluebubbles ` -- 配对是默认的令牌交换方式。详情:[配对](/channels/pairing) +- 配对是默认的令牌交换方式。详情见:[配对](/channels/pairing) 群组: @@ -89,13 +171,13 @@ openclaw channels add bluebubbles --http-url http://192.168.1.100:1234 --passwor ### 提及门控(群组) -BlueBubbles 支持群聊的提及门控,与 iMessage/WhatsApp 行为一致: +BlueBubbles 支持群聊中的提及门控,与 iMessage/WhatsApp 的行为一致: - 使用 `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)检测提及。 -- 当群组启用 `requireMention` 时,智能体仅在被提及时响应。 +- 当某个群组启用 `requireMention` 时,智能体只有在被提及时才会响应。 - 来自授权发送者的控制命令会绕过提及门控。 -单群组配置: +每群组配置: ```json5 { @@ -104,8 +186,8 @@ BlueBubbles 支持群聊的提及门控,与 iMessage/WhatsApp 行为一致: groupPolicy: "allowlist", groupAllowFrom: ["+15555550123"], groups: { - "*": { requireMention: true }, // 所有群组的默认设置 - "iMessage;-;chat123": { requireMention: false }, // 特定群组的覆盖设置 + "*": { requireMention: true }, // 所有群组的默认值 + "iMessage;-;chat123": { requireMention: false }, // 对特定群组覆盖 }, }, }, @@ -115,14 +197,14 @@ BlueBubbles 支持群聊的提及门控,与 iMessage/WhatsApp 行为一致: ### 命令门控 - 控制命令(例如 `/config`、`/model`)需要授权。 -- 使用 `allowFrom` 和 `groupAllowFrom` 确定命令授权。 -- 授权发送者即使在群组中未被提及也可以运行控制命令。 +- 使用 `allowFrom` 和 `groupAllowFrom` 来判断命令授权。 +- 授权发送者即使在群组中未提及,也可以运行控制命令。 ## 输入状态 + 已读回执 -- **输入指示器**:在响应生成前和生成期间自动发送。 +- **输入状态指示**:会在生成响应之前和期间自动发送。 - **已读回执**:由 `channels.bluebubbles.sendReadReceipts` 控制(默认:`true`)。 -- **输入指示器**:OpenClaw 发送输入开始事件;BlueBubbles 在发送或超时时自动清除输入状态(通过 DELETE 手动停止不可靠)。 +- **输入状态指示**:OpenClaw 会发送输入开始事件;BlueBubbles 会在发送后或超时后自动清除输入状态(通过 DELETE 手动停止并不可靠)。 ```json5 { @@ -136,7 +218,7 @@ BlueBubbles 支持群聊的提及门控,与 iMessage/WhatsApp 行为一致: ## 高级操作 -BlueBubbles 在配置中启用时支持高级消息操作: +在配置中启用后,BlueBubbles 支持高级消息操作: ```json5 { @@ -144,15 +226,15 @@ BlueBubbles 在配置中启用时支持高级消息操作: bluebubbles: { actions: { reactions: true, // tapback(默认:true) - edit: true, // 编辑已发送消息(macOS 13+,在 macOS 26 Tahoe 上不可用) + edit: true, // 编辑已发送消息(macOS 13+,在 macOS 26 Tahoe 上已损坏) unsend: true, // 撤回消息(macOS 13+) - reply: true, // 通过消息 GUID 进行回复线程 + reply: true, // 按消息 GUID 回复线程 sendWithEffect: true, // 消息效果(slam、loud 等) renameGroup: true, // 重命名群聊 setGroupIcon: true, // 设置群聊图标/照片(在 macOS 26 Tahoe 上不稳定) - addParticipant: true, // 将参与者添加到群组 + addParticipant: true, // 向群组添加参与者 removeParticipant: true, // 从群组移除参与者 - leaveGroup: true, // 离开群聊 + leaveGroup: true, // 退出群聊 sendAttachment: true, // 发送附件/媒体 }, }, @@ -163,37 +245,37 @@ BlueBubbles 在配置中启用时支持高级消息操作: 可用操作: - **react**:添加/移除 tapback 回应(`messageId`、`emoji`、`remove`) -- **edit**:编辑已发送的消息(`messageId`、`text`) +- **edit**:编辑已发送消息(`messageId`、`text`) - **unsend**:撤回消息(`messageId`) - **reply**:回复特定消息(`messageId`、`text`、`to`) - **sendWithEffect**:带 iMessage 效果发送(`text`、`to`、`effectId`) - **renameGroup**:重命名群聊(`chatGuid`、`displayName`) -- **setGroupIcon**:设置群聊图标/照片(`chatGuid`、`media`)— 在 macOS 26 Tahoe 上不稳定(API 可能返回成功但图标未同步)。 -- **addParticipant**:将某人添加到群组(`chatGuid`、`address`) -- **removeParticipant**:将某人从群组移除(`chatGuid`、`address`) -- **leaveGroup**:离开群聊(`chatGuid`) +- **setGroupIcon**:设置群聊图标/照片(`chatGuid`、`media`)——在 macOS 26 Tahoe 上不稳定(API 可能返回成功,但图标不会同步)。 +- **addParticipant**:向群组添加某人(`chatGuid`、`address`) +- **removeParticipant**:从群组移除某人(`chatGuid`、`address`) +- **leaveGroup**:退出群聊(`chatGuid`) - **sendAttachment**:发送媒体/文件(`to`、`buffer`、`filename`、`asVoice`) - - 语音备忘录:将 `asVoice: true` 与 **MP3** 或 **CAF** 音频一起设置,以 iMessage 语音消息形式发送。BlueBubbles 在发送语音备忘录时会将 MP3 转换为 CAF。 + - 语音备忘录:将 `asVoice: true` 与 **MP3** 或 **CAF** 音频一起设置,即可作为 iMessage 语音消息发送。BlueBubbles 在发送语音备忘录时会将 MP3 转换为 CAF。 -### 消息 ID(短格式 vs 完整格式) +### 消息 ID(短 ID 与完整 ID) OpenClaw 可能会显示*短*消息 ID(例如 `1`、`2`)以节省 token。 - `MessageSid` / `ReplyToId` 可以是短 ID。 - `MessageSidFull` / `ReplyToIdFull` 包含提供商的完整 ID。 -- 短 ID 存储在内存中;它们可能在重启或缓存清除后过期。 -- 操作接受短或完整的 `messageId`,但如果短 ID 不再可用将会报错。 +- 短 ID 存在于内存中;它们可能会在重启或缓存清除后失效。 +- 操作接受短或完整 `messageId`,但如果短 ID 不再可用,就会报错。 对于持久化自动化和存储,请使用完整 ID: - 模板:`{{MessageSidFull}}`、`{{ReplyToIdFull}}` - 上下文:入站负载中的 `MessageSidFull` / `ReplyToIdFull` -参见[配置](/gateway/configuration)了解模板变量。 +模板变量请参见[配置](/gateway/configuration)。 ## 分块流式传输 -控制响应是作为单条消息发送还是分块流式传输: +控制响应是作为单条消息发送,还是按块流式发送: ```json5 { @@ -208,8 +290,8 @@ OpenClaw 可能会显示*短*消息 ID(例如 `1`、`2`)以节省 token。 ## 媒体 + 限制 - 入站附件会被下载并存储在媒体缓存中。 -- 媒体上限通过 `channels.bluebubbles.mediaMaxMb` 设置(默认:8 MB)。 -- 出站文本按 `channels.bluebubbles.textChunkLimit` 分块(默认:4000 字符)。 +- 通过 `channels.bluebubbles.mediaMaxMb` 控制入站和出站媒体大小上限(默认:8 MB)。 +- 出站文本会按 `channels.bluebubbles.textChunkLimit` 分块(默认:4000 个字符)。 ## 配置参考 @@ -217,22 +299,23 @@ OpenClaw 可能会显示*短*消息 ID(例如 `1`、`2`)以节省 token。 提供商选项: -- `channels.bluebubbles.enabled`:启用/禁用渠道。 +- `channels.bluebubbles.enabled`:启用/禁用该渠道。 - `channels.bluebubbles.serverUrl`:BlueBubbles REST API 基础 URL。 - `channels.bluebubbles.password`:API 密码。 - `channels.bluebubbles.webhookPath`:Webhook 端点路径(默认:`/bluebubbles-webhook`)。 - `channels.bluebubbles.dmPolicy`:`pairing | allowlist | open | disabled`(默认:`pairing`)。 -- `channels.bluebubbles.allowFrom`:私信白名单(句柄、电子邮件、E.164 号码、`chat_id:*`、`chat_guid:*`)。 +- `channels.bluebubbles.allowFrom`:私信 allowlist(handle、电子邮件、E.164 号码、`chat_id:*`、`chat_guid:*`)。 - `channels.bluebubbles.groupPolicy`:`open | allowlist | disabled`(默认:`allowlist`)。 -- `channels.bluebubbles.groupAllowFrom`:群组发送者白名单。 -- `channels.bluebubbles.groups`:单群组配置(`requireMention` 等)。 +- `channels.bluebubbles.groupAllowFrom`:群组发送者 allowlist。 +- `channels.bluebubbles.groups`:每群组配置(`requireMention` 等)。 - `channels.bluebubbles.sendReadReceipts`:发送已读回执(默认:`true`)。 -- `channels.bluebubbles.blockStreaming`:启用分块流式传输(默认:`false`;流式回复必需)。 -- `channels.bluebubbles.textChunkLimit`:出站分块大小(字符)(默认:4000)。 -- `channels.bluebubbles.chunkMode`:`length`(默认)仅在超过 `textChunkLimit` 时分割;`newline` 在长度分块前先按空行(段落边界)分割。 -- `channels.bluebubbles.mediaMaxMb`:入站媒体上限(MB)(默认:8)。 -- `channels.bluebubbles.historyLimit`:上下文的最大群组消息数(0 表示禁用)。 -- `channels.bluebubbles.dmHistoryLimit`:私信历史限制。 +- `channels.bluebubbles.blockStreaming`:启用分块流式传输(默认:`false`;流式回复所必需)。 +- `channels.bluebubbles.textChunkLimit`:出站分块大小(按字符计,默认:4000)。 +- `channels.bluebubbles.chunkMode`:`length`(默认)仅在超过 `textChunkLimit` 时拆分;`newline` 会先按空行(段落边界)拆分,再按长度分块。 +- `channels.bluebubbles.mediaMaxMb`:入站/出站媒体大小上限(MB,默认:8)。 +- `channels.bluebubbles.mediaLocalRoots`:允许用于出站本地媒体路径的绝对本地目录显式 allowlist。默认情况下,本地路径发送会被拒绝,除非配置了此项。每账户覆盖:`channels.bluebubbles.accounts..mediaLocalRoots`。 +- `channels.bluebubbles.historyLimit`:用于上下文的最大群组消息数(0 表示禁用)。 +- `channels.bluebubbles.dmHistoryLimit`:私信历史记录上限。 - `channels.bluebubbles.actions`:启用/禁用特定操作。 - `channels.bluebubbles.accounts`:多账户配置。 @@ -241,31 +324,31 @@ OpenClaw 可能会显示*短*消息 ID(例如 `1`、`2`)以节省 token。 - `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)。 - `messages.responsePrefix`。 -## 地址 / 投递目标 +## 寻址 / 送达目标 -优先使用 `chat_guid` 以获得稳定的路由: +优先使用 `chat_guid` 以获得稳定路由: -- `chat_guid:iMessage;-;+15555550123`(群组推荐) +- `chat_guid:iMessage;-;+15555550123`(群组首选) - `chat_id:123` - `chat_identifier:...` -- 直接句柄:`+15555550123`、`user@example.com` - - 如果直接句柄没有现有的私信聊天,OpenClaw 将通过 `POST /api/v1/chat/new` 创建一个。这需要启用 BlueBubbles Private API。 +- 直接 handle:`+15555550123`、`user@example.com` + - 如果某个直接 handle 没有现有私信聊天,OpenClaw 会通过 `POST /api/v1/chat/new` 创建一个。这要求启用 BlueBubbles Private API。 -## 安全性 +## 安全 -- Webhook 请求通过比较 `guid`/`password` 查询参数或头部与 `channels.bluebubbles.password` 进行身份验证。来自 `localhost` 的请求也会被接受。 -- 保持 API 密码和 webhook 端点的机密性(将它们视为凭证)。 -- localhost 信任意味着同主机的反向代理可能无意中绕过密码验证。如果你使用代理 Gateway 网关,请在代理处要求身份验证并配置 `gateway.trustedProxies`。参见 [Gateway 网关安全性](/gateway/security#reverse-proxy-configuration)。 -- 如果将 BlueBubbles 服务器暴露在局域网之外,请启用 HTTPS + 防火墙规则。 +- 通过将查询参数或请求头中的 `guid`/`password` 与 `channels.bluebubbles.password` 进行比较来验证 webhook 请求。来自 `localhost` 的请求也会被接受。 +- 请妥善保管 API 密码和 webhook 端点(将它们视为凭证)。 +- 对 localhost 的信任意味着同主机上的反向代理可能会无意中绕过密码。如果你对 Gateway 网关进行了代理,请在代理层要求身份验证,并配置 `gateway.trustedProxies`。请参见 [Gateway 网关安全](/gateway/security#reverse-proxy-configuration)。 +- 如果要在局域网外暴露 BlueBubbles 服务器,请启用 HTTPS + 防火墙规则。 ## 故障排除 -- 如果输入/已读事件停止工作,请检查 BlueBubbles webhook 日志并验证 Gateway 网关路径是否与 `channels.bluebubbles.webhookPath` 匹配。 -- 配对码在一小时后过期;使用 `openclaw pairing list bluebubbles` 和 `openclaw pairing approve bluebubbles `。 -- 回应需要 BlueBubbles private API(`POST /api/v1/message/react`);确保服务器版本支持它。 -- 编辑/撤回需要 macOS 13+ 和兼容的 BlueBubbles 服务器版本。在 macOS 26(Tahoe)上,由于 private API 变更,编辑功能目前不可用。 -- 在 macOS 26(Tahoe)上群组图标更新可能不稳定:API 可能返回成功但新图标未同步。 -- OpenClaw 会根据 BlueBubbles 服务器的 macOS 版本自动隐藏已知不可用的操作。如果在 macOS 26(Tahoe)上编辑仍然显示,请使用 `channels.bluebubbles.actions.edit=false` 手动禁用。 -- 查看状态/健康信息:`openclaw status --all` 或 `openclaw status --deep`。 +- 如果输入状态/已读事件停止工作,请检查 BlueBubbles webhook 日志,并验证 Gateway 网关路径与 `channels.bluebubbles.webhookPath` 一致。 +- 配对码会在一小时后过期;使用 `openclaw pairing list bluebubbles` 和 `openclaw pairing approve bluebubbles `。 +- 回应需要 BlueBubbles private API(`POST /api/v1/message/react`);请确保服务器版本提供该接口。 +- 编辑/撤回需要 macOS 13+ 以及兼容的 BlueBubbles 服务器版本。在 macOS 26(Tahoe)上,由于 private API 变更,编辑功能目前已损坏。 +- 群组图标更新在 macOS 26(Tahoe)上可能不稳定:API 可能返回成功,但新图标不会同步。 +- OpenClaw 会根据 BlueBubbles 服务器的 macOS 版本自动隐藏已知损坏的操作。如果在 macOS 26(Tahoe)上仍显示编辑操作,请手动使用 `channels.bluebubbles.actions.edit=false` 禁用它。 +- 状态/健康信息请使用:`openclaw status --all` 或 `openclaw status --deep`。 -有关通用渠道工作流参考,请参阅[渠道](/channels)和[插件](/tools/plugin)指南。 +有关通用渠道工作流程参考,请参见 [Channels](/channels) 和 [Plugins](/tools/plugin) 指南。 diff --git a/docs/zh-CN/channels/feishu.md b/docs/zh-CN/channels/feishu.md index 6a8d8633af9..af561e3a8ea 100644 --- a/docs/zh-CN/channels/feishu.md +++ b/docs/zh-CN/channels/feishu.md @@ -1,22 +1,29 @@ --- -summary: "飞书机器人支持状态、功能和配置" read_when: - - 您想要连接飞书机器人 - - 您正在配置飞书渠道 + - 你想连接一个飞书/Lark 机器人 + - 你正在配置飞书渠道 +summary: 飞书机器人概览、功能和配置 title: 飞书 +x-i18n: + generated_at: "2026-03-16T06:21:11Z" + model: gpt-5.4 + provider: openai + source_hash: 951e78c5c7264471382f863fa896a15ddeaf0717ef782da20d0f1b3eb23396ba + source_path: channels/feishu.md + workflow: 15 --- # 飞书机器人 -状态:生产就绪,支持机器人私聊和群组。使用 WebSocket 长连接模式接收消息。 +飞书(Lark)是企业用于消息沟通与协作的团队聊天平台。此插件通过平台的 WebSocket 事件订阅将 OpenClaw 连接到飞书/Lark 机器人,因此无需暴露公共 webhook URL 即可接收消息。 --- -## 内置插件 +## 捆绑插件 -当前版本的 OpenClaw 已内置 Feishu 插件,因此通常不需要单独安装。 +飞书随当前的 OpenClaw 版本一同捆绑提供,因此无需单独安装插件。 -如果你使用的是较旧版本,或是没有内置 Feishu 的自定义安装,可手动安装: +如果你使用的是较旧版本,或使用了不包含捆绑飞书的自定义安装,请手动安装: ```bash openclaw plugins install @openclaw/feishu @@ -26,75 +33,75 @@ openclaw plugins install @openclaw/feishu ## 快速开始 -添加飞书渠道有两种方式: +有两种方式可添加飞书渠道: -### 方式一:通过安装向导添加(推荐) +### 方法 1:设置向导(推荐) -如果您刚安装完 OpenClaw,可以直接运行向导,根据提示添加飞书: +如果你刚安装 OpenClaw,请运行设置向导: ```bash openclaw onboard ``` -向导会引导您完成: +向导会引导你完成以下步骤: -1. 创建飞书应用并获取凭证 -2. 配置应用凭证 -3. 启动网关 +1. 创建飞书应用并收集凭证 +2. 在 OpenClaw 中配置应用凭证 +3. 启动 Gateway 网关 -✅ **完成配置后**,您可以使用以下命令检查网关状态: +✅ **配置完成后**,检查 Gateway 网关状态: -- `openclaw gateway status` - 查看网关运行状态 -- `openclaw logs --follow` - 查看实时日志 +- `openclaw gateway status` +- `openclaw logs --follow` -### 方式二:通过命令行添加 +### 方法 2:CLI 设置 -如果您已经完成了初始安装,可以用以下命令添加飞书渠道: +如果你已经完成初始安装,可通过 CLI 添加该渠道: ```bash openclaw channels add ``` -然后根据交互式提示选择 Feishu,输入 App ID 和 App Secret 即可。 +选择 **Feishu**,然后输入 App ID 和 App Secret。 -✅ **完成配置后**,您可以使用以下命令管理网关: +✅ **配置完成后**,管理 Gateway 网关: -- `openclaw gateway status` - 查看网关运行状态 -- `openclaw gateway restart` - 重启网关以应用新配置 -- `openclaw logs --follow` - 查看实时日志 +- `openclaw gateway status` +- `openclaw gateway restart` +- `openclaw logs --follow` --- -## 第一步:创建飞书应用 +## 第 1 步:创建飞书应用 ### 1. 打开飞书开放平台 -访问 [飞书开放平台](https://open.feishu.cn/app),使用飞书账号登录。 +访问 [Feishu Open Platform](https://open.feishu.cn/app) 并登录。 -Lark(国际版)请使用 https://open.larksuite.com/app,并在配置中设置 `domain: "lark"`。 +Lark(国际版)租户应使用 [https://open.larksuite.com/app](https://open.larksuite.com/app),并在飞书配置中设置 `domain: "lark"`。 ### 2. 创建应用 -1. 点击 **创建企业自建应用** +1. 点击 **Create enterprise app** 2. 填写应用名称和描述 3. 选择应用图标 -![创建企业自建应用](/images/feishu-step2-create-app.png) +![Create enterprise app](../images/feishu-step2-create-app.png) -### 3. 获取应用凭证 +### 3. 复制凭证 -在应用的 **凭证与基础信息** 页面,复制: +在 **Credentials & Basic Info** 中,复制: -- **App ID**(格式如 `cli_xxx`) +- **App ID**(格式:`cli_xxx`) - **App Secret** -❗ **重要**:请妥善保管 App Secret,不要分享给他人。 +❗ **重要:**请将 App Secret 妥善保密。 -![获取应用凭证](/images/feishu-step3-credentials.png) +![Get credentials](../images/feishu-step3-credentials.png) -### 4. 配置应用权限 +### 4. 配置权限 -在 **权限管理** 页面,点击 **批量导入** 按钮,粘贴以下 JSON 配置一键导入所需权限: +在 **Permissions** 中,点击 **Batch import** 并粘贴: ```json { @@ -105,81 +112,71 @@ Lark(国际版)请使用 https://open.larksuite.com/app,并在配置中设 "application:application.app_message_stats.overview:readonly", "application:application:self_manage", "application:bot.menu:write", + "cardkit:card:read", "cardkit:card:write", "contact:user.employee_id:readonly", "corehr:file:download", - "docs:document.content:read", "event:ip_list", - "im:chat", "im:chat.access_event.bot_p2p_chat:read", "im:chat.members:bot_access", "im:message", "im:message.group_at_msg:readonly", - "im:message.group_msg", "im:message.p2p_msg:readonly", "im:message:readonly", "im:message:send_as_bot", - "im:resource", - "sheets:spreadsheet", - "wiki:wiki:readonly" + "im:resource" ], "user": ["aily:file:read", "aily:file:write", "im:chat.access_event.bot_p2p_chat:read"] } } ``` -![配置应用权限](/images/feishu-step4-permissions.png) +![Configure permissions](../images/feishu-step4-permissions.png) ### 5. 启用机器人能力 -在 **应用能力** > **机器人** 页面: +在 **App Capability** > **Bot** 中: -1. 开启机器人能力 -2. 配置机器人名称 +1. 启用机器人能力 +2. 设置机器人名称 -![启用机器人能力](/images/feishu-step5-bot-capability.png) +![Enable bot capability](../images/feishu-step5-bot-capability.png) ### 6. 配置事件订阅 -⚠️ **重要提醒**:在配置事件订阅前,请务必确保已完成以下步骤: +⚠️ **重要:**在设置事件订阅前,请确保: -1. 运行 `openclaw channels add` 添加了 Feishu 渠道 -2. 网关处于启动状态(可通过 `openclaw gateway status` 检查状态) +1. 你已经为飞书运行过 `openclaw channels add` +2. Gateway 网关正在运行(`openclaw gateway status`) -在 **事件订阅** 页面: +在 **Event Subscription** 中: -1. 选择 **使用长连接接收事件**(WebSocket 模式) -2. 添加事件: - - `im.message.receive_v1` - - `im.message.reaction.created_v1` - - `im.message.reaction.deleted_v1` - - `application.bot.menu_v6` +1. 选择 **Use long connection to receive events**(WebSocket) +2. 添加事件:`im.message.receive_v1` -⚠️ **注意**:如果网关未启动或渠道未添加,长连接设置将保存失败。 +⚠️ 如果 Gateway 网关未运行,长连接设置可能无法保存。 -![配置事件订阅](/images/feishu-step6-event-subscription.png) +![Configure event subscription](../images/feishu-step6-event-subscription.png) ### 7. 发布应用 -1. 在 **版本管理与发布** 页面创建版本 +1. 在 **Version Management & Release** 中创建版本 2. 提交审核并发布 -3. 等待管理员审批(企业自建应用通常自动通过) +3. 等待管理员批准(企业应用通常会自动批准) --- -## 第二步:配置 OpenClaw +## 第 2 步:配置 OpenClaw -### 通过向导配置(推荐) - -运行以下命令,根据提示粘贴 App ID 和 App Secret: +### 使用向导配置(推荐) ```bash openclaw channels add ``` -选择 **Feishu**,然后输入您在第一步获取的凭证即可。 +选择 **Feishu**,然后粘贴你的 App ID 和 App Secret。 -### 通过配置文件配置 +### 通过配置文件进行配置 编辑 `~/.openclaw/openclaw.json`: @@ -193,7 +190,7 @@ openclaw channels add main: { appId: "cli_xxx", appSecret: "xxx", - botName: "我的AI助手", + botName: "My AI assistant", }, }, }, @@ -201,18 +198,20 @@ openclaw channels add } ``` -若使用 `connectionMode: "webhook"`,需设置 `verificationToken`。飞书 Webhook 服务默认绑定 `127.0.0.1`;仅在需要不同监听地址时设置 `webhookHost`。 +如果你使用 `connectionMode: "webhook"`,请同时设置 `verificationToken` 和 `encryptKey`。飞书 webhook 服务器默认绑定到 `127.0.0.1`;只有在你明确需要不同绑定地址时,才设置 `webhookHost`。 -#### 获取 Verification Token(仅 Webhook 模式) +#### Verification Token 和 Encrypt Key(webhook 模式) -使用 Webhook 模式时,需在配置中设置 `channels.feishu.verificationToken`。获取方式: +使用 webhook 模式时,请在配置中同时设置 `channels.feishu.verificationToken` 和 `channels.feishu.encryptKey`。获取这些值的方法如下: -1. 在飞书开放平台打开您的应用 -2. 进入 **开发配置** → **事件与回调** -3. 打开 **加密策略** 选项卡 -4. 复制 **Verification Token**(校验令牌) +1. 在飞书开放平台中,打开你的应用 +2. 前往 **Development** → **Events & Callbacks**(开发配置 → 事件与回调) +3. 打开 **Encryption** 标签页(加密策略) +4. 复制 **Verification Token** 和 **Encrypt Key** -![Verification Token 位置](/images/feishu-verification-token.png) +下图展示了 **Verification Token** 的位置。**Encrypt Key** 位于同一个 **Encryption** 区域中。 + +![Verification Token location](../images/feishu-verification-token.png) ### 通过环境变量配置 @@ -223,7 +222,7 @@ export FEISHU_APP_SECRET="xxx" ### Lark(国际版)域名 -如果您的租户在 Lark(国际版),请设置域名为 `lark`(或完整域名),可配置 `channels.feishu.domain` 或 `channels.feishu.accounts..domain`: +如果你的租户位于 Lark(国际版),请将域名设置为 `lark`(或完整域名字串)。你可以在 `channels.feishu.domain` 设置,也可以按账户设置(`channels.feishu.accounts..domain`)。 ```json5 { @@ -241,14 +240,14 @@ export FEISHU_APP_SECRET="xxx" } ``` -### 配额优化 +### 配额优化标志 -可通过以下可选配置减少飞书 API 调用: +你可以使用两个可选标志来减少飞书 API 使用量: -- `typingIndicator`(默认 `true`):设为 `false` 时不发送“正在输入”状态。 -- `resolveSenderNames`(默认 `true`):设为 `false` 时不拉取发送者资料。 +- `typingIndicator`(默认 `true`):设为 `false` 时,跳过“正在输入”反应调用。 +- `resolveSenderNames`(默认 `true`):设为 `false` 时,跳过发送者资料查询调用。 -可在渠道级或账号级配置: +你可以在顶层或按账户进行设置: ```json5 { @@ -271,9 +270,9 @@ export FEISHU_APP_SECRET="xxx" --- -## 第三步:启动并测试 +## 第 3 步:启动并测试 -### 1. 启动网关 +### 1. 启动 Gateway 网关 ```bash openclaw gateway @@ -281,74 +280,74 @@ openclaw gateway ### 2. 发送测试消息 -在飞书中找到您创建的机器人,发送一条消息。 +在飞书中找到你的机器人并发送一条消息。 -### 3. 配对授权 +### 3. 批准配对 -默认情况下,机器人会回复一个 **配对码**。您需要批准此代码: +默认情况下,机器人会回复一个配对码。批准它: ```bash -openclaw pairing approve feishu <配对码> +openclaw pairing approve feishu ``` -批准后即可正常对话。 +批准后,你就可以正常聊天了。 --- -## 介绍 +## 概览 -- **飞书机器人渠道**:由网关管理的飞书机器人 -- **确定性路由**:回复始终返回飞书,模型不会选择渠道 -- **会话隔离**:私聊共享主会话;群组独立隔离 -- **WebSocket 连接**:使用飞书 SDK 的长连接模式,无需公网 URL +- **飞书机器人渠道**:由 Gateway 网关管理的飞书机器人 +- **确定性路由**:回复始终返回到飞书 +- **会话隔离**:私信共享主会话;群组彼此隔离 +- **WebSocket 连接**:通过飞书 SDK 建立长连接,无需公共 URL --- ## 访问控制 -### 私聊访问 +### 私信 -- **默认**:`dmPolicy: "pairing"`,陌生用户会收到配对码 +- **默认**:`dmPolicy: "pairing"`(未知用户会收到配对码) - **批准配对**: - ```bash - openclaw pairing list feishu # 查看待审批列表 - openclaw pairing approve feishu # 批准 - ``` -- **白名单模式**:通过 `channels.feishu.allowFrom` 配置允许的用户 Open ID -### 群组访问 + ```bash + openclaw pairing list feishu + openclaw pairing approve feishu + ``` + +- **Allowlist 模式**:设置 `channels.feishu.allowFrom`,填入允许的 Open ID + +### 群聊 **1. 群组策略**(`channels.feishu.groupPolicy`): -- `"open"` = 允许群组中所有人(默认) -- `"allowlist"` = 仅允许 `groupAllowFrom` 中的群组 -- `"disabled"` = 禁用群组消息 +- `"open"` = 允许群组中的所有人(默认) +- `"allowlist"` = 仅允许 `groupAllowFrom` +- `"disabled"` = 禁用群消息 -**2. @提及要求**(`channels.feishu.groups..requireMention`): +**2. 提及要求**(`channels.feishu.groups..requireMention`): -- `true` = 需要 @机器人才响应(默认) -- `false` = 无需 @也响应 +- `true` = 需要 @ 提及(默认) +- `false` = 无需提及也会回复 --- ## 群组配置示例 -### 允许所有群组,需要 @提及(默认行为) +### 允许所有群组,要求 @ 提及(默认) ```json5 { channels: { feishu: { groupPolicy: "open", - // 默认 requireMention: true + // Default requireMention: true }, }, } ``` -### 允许所有群组,无需 @提及 - -需要为特定群组配置: +### 允许所有群组,无需 @ 提及 ```json5 { @@ -369,16 +368,16 @@ openclaw pairing approve feishu <配对码> channels: { feishu: { groupPolicy: "allowlist", - // 群组 ID 格式为 oc_xxx + // Feishu group IDs (chat_id) look like: oc_xxx groupAllowFrom: ["oc_xxx", "oc_yyy"], }, }, } ``` -### 仅允许特定成员在群组中发信(发送者白名单) +### 限制哪些发送者可以在群组中发消息(发送者 allowlist) -除群组白名单外,该群组内**所有消息**均按发送者 open_id 校验:仅 `groups..allowFrom` 中列出的用户消息会被处理,其他成员的消息会被忽略(此为发送者级白名单,不仅针对 /reset、/new 等控制命令)。 +除了允许群组本身外,该群组中的**所有消息**还会按发送者 `open_id` 进行限制:只有列在 `groups..allowFrom` 中的用户,其消息才会被处理;其他成员发送的消息会被忽略(这是完整的发送者级限制,而不只是对 `/reset` 或 `/new` 等控制命令生效)。 ```json5 { @@ -388,7 +387,7 @@ openclaw pairing approve feishu <配对码> groupAllowFrom: ["oc_xxx"], groups: { oc_xxx: { - // 用户 open_id 格式为 ou_xxx + // Feishu user IDs (open_id) look like: ou_xxx allowFrom: ["ou_user1", "ou_user2"], }, }, @@ -401,29 +400,31 @@ openclaw pairing approve feishu <配对码> ## 获取群组/用户 ID -### 获取群组 ID(chat_id) +### 群组 ID(`chat_id`) -群组 ID 格式为 `oc_xxx`,可以通过以下方式获取: +群组 ID 看起来像 `oc_xxx`。 -**方法一**(推荐): +**方法 1(推荐)** -1. 启动网关并在群组中 @机器人发消息 -2. 运行 `openclaw logs --follow` 查看日志中的 `chat_id` +1. 启动 Gateway 网关并在群里 @ 提及机器人 +2. 运行 `openclaw logs --follow` 并查找 `chat_id` -**方法二**: -使用飞书 API 调试工具获取机器人所在群组列表。 +**方法 2** -### 获取用户 ID(open_id) +使用飞书 API 调试器列出群聊。 -用户 ID 格式为 `ou_xxx`,可以通过以下方式获取: +### 用户 ID(`open_id`) -**方法一**(推荐): +用户 ID 看起来像 `ou_xxx`。 -1. 启动网关并给机器人发消息 -2. 运行 `openclaw logs --follow` 查看日志中的 `open_id` +**方法 1(推荐)** -**方法二**: -查看配对请求列表,其中包含用户的 Open ID: +1. 启动 Gateway 网关并向机器人发送私信 +2. 运行 `openclaw logs --follow` 并查找 `open_id` + +**方法 2** + +检查配对请求中的用户 Open ID: ```bash openclaw pairing list feishu @@ -433,65 +434,61 @@ openclaw pairing list feishu ## 常用命令 -| 命令 | 说明 | +| Command | Description | | --------- | -------------- | -| `/status` | 查看机器人状态 | -| `/reset` | 重置对话会话 | -| `/model` | 查看/切换模型 | +| `/status` | 显示机器人状态 | +| `/reset` | 重置会话 | +| `/model` | 显示/切换模型 | -飞书机器人菜单建议直接在飞书开放平台的机器人能力页面配置。OpenClaw 当前支持接收 `application.bot.menu_v6` 事件,并把点击事件转换成普通文本命令(例如 `/menu `)继续走现有消息路由,但不通过渠道配置自动创建或同步菜单。 +> 注意:飞书暂不支持原生命令菜单,因此命令必须以文本形式发送。 -## 网关管理命令 +## Gateway 网关管理命令 -在配置和使用飞书渠道时,您可能需要使用以下网关管理命令: - -| 命令 | 说明 | -| -------------------------- | ----------------- | -| `openclaw gateway status` | 查看网关运行状态 | -| `openclaw gateway install` | 安装/启动网关服务 | -| `openclaw gateway stop` | 停止网关服务 | -| `openclaw gateway restart` | 重启网关服务 | -| `openclaw logs --follow` | 实时查看日志输出 | +| Command | Description | +| -------------------------- | -------------------------- | +| `openclaw gateway status` | 显示 Gateway 网关状态 | +| `openclaw gateway install` | 安装/启动 Gateway 网关服务 | +| `openclaw gateway stop` | 停止 Gateway 网关服务 | +| `openclaw gateway restart` | 重启 Gateway 网关服务 | +| `openclaw logs --follow` | 跟踪 Gateway 网关日志 | --- ## 故障排除 -### 机器人在群组中不响应 +### 机器人在群聊中没有响应 -1. 检查机器人是否已添加到群组 -2. 检查是否 @了机器人(默认需要 @提及) -3. 检查 `groupPolicy` 是否为 `"disabled"` -4. 查看日志:`openclaw logs --follow` +1. 确保机器人已加入群组 +2. 确保你 @ 提及了机器人(默认行为) +3. 检查 `groupPolicy` 未设置为 `"disabled"` +4. 检查日志:`openclaw logs --follow` -### 机器人收不到消息 +### 机器人未接收到消息 -1. 检查应用是否已发布并审批通过 -2. 检查事件订阅是否配置正确(`im.message.receive_v1`) -3. 检查是否选择了 **长连接** 模式 -4. 检查应用权限是否完整 -5. 检查网关是否正在运行:`openclaw gateway status` -6. 查看实时日志:`openclaw logs --follow` +1. 确保应用已发布并获批准 +2. 确保事件订阅包含 `im.message.receive_v1` +3. 确保已启用**长连接** +4. 确保应用权限完整 +5. 确保 Gateway 网关正在运行:`openclaw gateway status` +6. 检查日志:`openclaw logs --follow` -### App Secret 泄露怎么办 +### App Secret 泄露 -1. 在飞书开放平台重置 App Secret -2. 更新配置文件中的 App Secret -3. 重启网关 +1. 在飞书开放平台中重置 App Secret +2. 在你的配置中更新 App Secret +3. 重启 Gateway 网关 -### 发送消息失败 +### 消息发送失败 -1. 检查应用是否有 `im:message:send_as_bot` 权限 -2. 检查应用是否已发布 -3. 查看日志获取详细错误信息 +1. 确保应用具有 `im:message:send_as_bot` 权限 +2. 确保应用已发布 +3. 查看日志以获取详细错误信息 --- ## 高级配置 -### 多账号配置 - -如果需要管理多个飞书机器人,可配置 `defaultAccount` 指定出站未显式指定 `accountId` 时使用的账号: +### 多账户 ```json5 { @@ -502,13 +499,13 @@ openclaw pairing list feishu main: { appId: "cli_xxx", appSecret: "xxx", - botName: "主机器人", + botName: "Primary bot", }, backup: { appId: "cli_yyy", appSecret: "yyy", - botName: "备用机器人", - enabled: false, // 暂时禁用 + botName: "Backup bot", + enabled: false, }, }, }, @@ -516,104 +513,102 @@ openclaw pairing list feishu } ``` +`defaultAccount` 用于控制当出站 API 未显式指定 `accountId` 时,使用哪个飞书账户。 + ### 消息限制 -- `textChunkLimit`:出站文本分块大小(默认 2000 字符) -- `mediaMaxMb`:媒体上传/下载限制(默认 30MB) +- `textChunkLimit`:出站文本分块大小(默认:2000 个字符) +- `mediaMaxMb`:媒体上传/下载限制(默认:30 MB) -### 流式输出 +### 流式传输 -飞书支持通过交互式卡片实现流式输出,机器人会实时更新卡片内容显示生成进度。默认配置: +飞书通过交互式卡片支持流式回复。启用后,机器人会在生成文本时更新卡片。 ```json5 { channels: { feishu: { streaming: true, // 启用流式卡片输出(默认 true) - blockStreamingCoalesce: { - enabled: true, - minDelayMs: 50, - maxDelayMs: 250, - }, + blockStreaming: true, // 启用分块流式传输(默认 true) }, }, } ``` -如需禁用流式输出(等待完整回复后一次性发送),可设置 `streaming: false`。 +将 `streaming: false` 设为等待完整回复生成后再发送。 -### 交互式卡片 +### ACP 会话 -OpenClaw 默认会在需要时发送 Markdown 卡片;如果你需要完整的 Feishu 原生交互式卡片,也可以显式发送原始 `card` payload。 +飞书支持以下 ACP 场景: -- 默认路径:文本自动渲染或 Markdown 卡片 -- 显式卡片:通过消息动作的 `card` 参数发送原始交互卡片 -- 更新卡片:同一消息支持后续 patch/update +- 私信 +- 群组话题会话 -卡片按钮回调当前走文本回退路径: +飞书 ACP 由文本命令驱动。没有原生斜杠命令菜单,因此请直接在会话中使用 `/acp ...` 消息。 -- 若 `action.value.text` 存在,则作为入站文本继续处理 -- 若 `action.value.command` 存在,则作为命令文本继续处理 -- 其他对象值会序列化为 JSON 文本 +#### 持久化 ACP 绑定 -这样可以保持与现有消息/命令路由兼容,而不要求下游先理解 Feishu 专有的交互 payload。 - -### 表情反应 - -飞书渠道现已完整支持表情反应生命周期: - -- 接收 `reaction created` -- 接收 `reaction deleted` -- 主动添加反应 -- 主动删除自身反应 -- 查询消息上的反应列表 - -是否把入站反应转成内部消息,可通过 `reactionNotifications` 控制: - -| 值 | 行为 | -| ----- | ---------------------------- | -| `off` | 不生成反应通知 | -| `own` | 仅当反应发生在机器人消息上时 | -| `all` | 所有可验证的反应都生成通知 | - -### 消息引用 - -在群聊中,机器人的回复可以引用用户发送的原始消息,让对话上下文更加清晰。 - -配置选项: +使用顶层类型化 ACP 绑定,将飞书私信或话题会话固定到持久化 ACP 会话。 ```json5 { - channels: { - feishu: { - // 账户级别配置(默认 "all") - replyToMode: "all", - groups: { - oc_xxx: { - // 特定群组可以覆盖 - replyToMode: "first", + agents: { + list: [ + { + id: "codex", + runtime: { + type: "acp", + acp: { + agent: "codex", + backend: "acpx", + mode: "persistent", + cwd: "/workspace/openclaw", + }, }, }, - }, + ], }, + bindings: [ + { + type: "acp", + agentId: "codex", + match: { + channel: "feishu", + accountId: "default", + peer: { kind: "direct", id: "ou_1234567890" }, + }, + }, + { + type: "acp", + agentId: "codex", + match: { + channel: "feishu", + accountId: "default", + peer: { kind: "group", id: "oc_group_chat:topic:om_topic_root" }, + }, + acp: { label: "codex-feishu-topic" }, + }, + ], } ``` -`replyToMode` 值说明: +#### 从聊天中按线程绑定 ACP 生成 -| 值 | 行为 | -| --------- | ---------------------------------- | -| `"off"` | 不引用原消息(私聊默认值) | -| `"first"` | 仅在第一条回复时引用原消息 | -| `"all"` | 所有回复都引用原消息(群聊默认值) | +在飞书私信或话题会话中,你可以就地生成并绑定一个 ACP 会话: -> 注意:消息引用功能与流式卡片输出(`streaming: true`)不能同时使用。当启用流式输出时,回复会以卡片形式呈现,不会显示引用。 +```text +/acp spawn codex --thread here +``` -### 多 Agent 路由 +说明: -通过 `bindings` 配置,您可以用一个飞书机器人对接多个不同功能或性格的 Agent。系统会根据用户 ID 或群组 ID 自动将对话分发到对应的 Agent。 +- `--thread here` 适用于私信和飞书话题。 +- 绑定后的私信/话题中的后续消息会直接路由到该 ACP 会话。 +- v1 不支持针对通用的非话题群聊。 -配置示例: +### 多智能体路由 + +使用 `bindings` 将飞书私信或群组路由到不同的智能体。 ```json5 { @@ -634,91 +629,81 @@ OpenClaw 默认会在需要时发送 Markdown 卡片;如果你需要完整的 }, bindings: [ { - // 用户 A 的私聊 → main agent agentId: "main", match: { channel: "feishu", - peer: { kind: "dm", id: "ou_28b31a88..." }, + peer: { kind: "direct", id: "ou_xxx" }, }, }, { - // 用户 B 的私聊 → clawd-fan agent agentId: "clawd-fan", match: { channel: "feishu", - peer: { kind: "dm", id: "ou_0fe6b1c9..." }, + peer: { kind: "direct", id: "ou_yyy" }, }, }, { - // 某个群组 → clawd-xi agent agentId: "clawd-xi", match: { channel: "feishu", - peer: { kind: "group", id: "oc_xxx..." }, + peer: { kind: "group", id: "oc_zzz" }, }, }, ], } ``` -匹配规则说明: +路由字段: -| 字段 | 说明 | -| ----------------- | --------------------------------------------- | -| `agentId` | 目标 Agent 的 ID,需要在 `agents.list` 中定义 | -| `match.channel` | 渠道类型,这里固定为 `"feishu"` | -| `match.peer.kind` | 对话类型:`"dm"`(私聊)或 `"group"`(群组) | -| `match.peer.id` | 用户 Open ID(`ou_xxx`)或群组 ID(`oc_xxx`) | +- `match.channel`:`"feishu"` +- `match.peer.kind`:`"direct"` 或 `"group"` +- `match.peer.id`:用户 Open ID(`ou_xxx`)或群组 ID(`oc_xxx`) -> 获取 ID 的方法:参见上文 [获取群组/用户 ID](#获取群组用户-id) 章节。 +查找提示请参见 [获取群组/用户 ID](#get-groupuser-ids)。 --- ## 配置参考 -完整配置请参考:[网关配置](/gateway/configuration) +完整配置:[Gateway 网关配置](/gateway/configuration) -主要选项: +关键选项: -| 配置项 | 说明 | 默认值 | -| ------------------------------------------------- | --------------------------------- | ---------------- | -| `channels.feishu.enabled` | 启用/禁用渠道 | `true` | -| `channels.feishu.domain` | API 域名(`feishu` 或 `lark`) | `feishu` | -| `channels.feishu.connectionMode` | 事件传输模式(websocket/webhook) | `websocket` | -| `channels.feishu.defaultAccount` | 出站路由默认账号 ID | `default` | -| `channels.feishu.verificationToken` | Webhook 模式必填 | - | -| `channels.feishu.webhookPath` | Webhook 路由路径 | `/feishu/events` | -| `channels.feishu.webhookHost` | Webhook 监听地址 | `127.0.0.1` | -| `channels.feishu.webhookPort` | Webhook 监听端口 | `3000` | -| `channels.feishu.accounts..appId` | 应用 App ID | - | -| `channels.feishu.accounts..appSecret` | 应用 App Secret | - | -| `channels.feishu.accounts..domain` | 单账号 API 域名覆盖 | `feishu` | -| `channels.feishu.dmPolicy` | 私聊策略 | `pairing` | -| `channels.feishu.allowFrom` | 私聊白名单(open_id 列表) | - | -| `channels.feishu.groupPolicy` | 群组策略 | `allowlist` | -| `channels.feishu.groupAllowFrom` | 群组白名单 | - | -| `channels.feishu.groups..requireMention` | 是否需要 @提及 | `true` | -| `channels.feishu.groups..enabled` | 是否启用该群组 | `true` | -| `channels.feishu.replyInThread` | 群聊回复是否进入飞书话题线程 | `disabled` | -| `channels.feishu.groupSessionScope` | 群聊会话隔离粒度 | `group` | -| `channels.feishu.textChunkLimit` | 消息分块大小 | `2000` | -| `channels.feishu.mediaMaxMb` | 媒体大小限制 | `30` | -| `channels.feishu.streaming` | 启用流式卡片输出 | `true` | -| `channels.feishu.blockStreamingCoalesce.enabled` | 启用块级流式合并 | `true` | -| `channels.feishu.typingIndicator` | 发送“正在输入”状态 | `true` | -| `channels.feishu.resolveSenderNames` | 拉取发送者名称 | `true` | -| `channels.feishu.reactionNotifications` | 入站反应通知策略 | `own` | +| Setting | Description | Default | +| ------------------------------------------------- | -------------------------------- | ---------------- | +| `channels.feishu.enabled` | 启用/禁用渠道 | `true` | +| `channels.feishu.domain` | API 域名(`feishu` 或 `lark`) | `feishu` | +| `channels.feishu.connectionMode` | 事件传输模式 | `websocket` | +| `channels.feishu.defaultAccount` | 出站路由的默认账户 ID | `default` | +| `channels.feishu.verificationToken` | webhook 模式必填 | - | +| `channels.feishu.encryptKey` | webhook 模式必填 | - | +| `channels.feishu.webhookPath` | webhook 路由路径 | `/feishu/events` | +| `channels.feishu.webhookHost` | webhook 绑定主机 | `127.0.0.1` | +| `channels.feishu.webhookPort` | webhook 绑定端口 | `3000` | +| `channels.feishu.accounts..appId` | App ID | - | +| `channels.feishu.accounts..appSecret` | App Secret | - | +| `channels.feishu.accounts..domain` | 按账户覆盖 API 域名 | `feishu` | +| `channels.feishu.dmPolicy` | 私信策略 | `pairing` | +| `channels.feishu.allowFrom` | 私信 allowlist(`open_id` 列表) | - | +| `channels.feishu.groupPolicy` | 群组策略 | `open` | +| `channels.feishu.groupAllowFrom` | 群组 allowlist | - | +| `channels.feishu.groups..requireMention` | 要求 @ 提及 | `true` | +| `channels.feishu.groups..enabled` | 启用群组 | `true` | +| `channels.feishu.textChunkLimit` | 消息分块大小 | `2000` | +| `channels.feishu.mediaMaxMb` | 媒体大小限制 | `30` | +| `channels.feishu.streaming` | 启用流式卡片输出 | `true` | +| `channels.feishu.blockStreaming` | 启用分块流式传输 | `true` | --- -## dmPolicy 策略说明 +## dmPolicy 参考 -| 值 | 行为 | -| ------------- | -------------------------------------------------- | -| `"pairing"` | **默认**。未知用户收到配对码,管理员批准后才能对话 | -| `"allowlist"` | 仅 `allowFrom` 列表中的用户可对话,其他静默忽略 | -| `"open"` | 允许所有人对话(需在 allowFrom 中加 `"*"`) | -| `"disabled"` | 完全禁止私聊 | +| Value | Behavior | +| ------------- | ---------------------------------------------------- | +| `"pairing"` | **默认。**未知用户会收到配对码;必须获批准后才能使用 | +| `"allowlist"` | 只有 `allowFrom` 中的用户可以聊天 | +| `"open"` | 允许所有用户(要求 `allowFrom` 中有 `"*"`) | +| `"disabled"` | 禁用私信 | --- @@ -726,17 +711,17 @@ OpenClaw 默认会在需要时发送 Markdown 卡片;如果你需要完整的 ### 接收 -- ✅ 文本消息 -- ✅ 富文本(帖子) +- ✅ 文本 +- ✅ 富文本(post) - ✅ 图片 - ✅ 文件 - ✅ 音频 - ✅ 视频 -- ✅ 表情包 +- ✅ 贴纸 ### 发送 -- ✅ 文本消息 +- ✅ 文本 - ✅ 图片 - ✅ 文件 - ✅ 音频 diff --git a/docs/zh-CN/channels/nostr.md b/docs/zh-CN/channels/nostr.md index 7d0359ce21d..47efa40aedf 100644 --- a/docs/zh-CN/channels/nostr.md +++ b/docs/zh-CN/channels/nostr.md @@ -1,14 +1,14 @@ --- read_when: - - 你希望 OpenClaw 通过 Nostr 接收私信 - - 你正在设置去中心化消息 -summary: 通过 NIP-04 加密消息的 Nostr 私信渠道 + - 你想让 OpenClaw 通过 Nostr 接收私信 + - 你正在设置去中心化消息传递 +summary: 通过 NIP-04 加密消息实现的 Nostr 私信渠道 title: Nostr x-i18n: - generated_at: "2026-02-03T07:44:13Z" - model: claude-opus-4-5 - provider: pi - source_hash: 6b9fe4c74bf5e7c0f59bbaa129ec5270fd29a248551a8a9a7dde6cff8fb46111 + generated_at: "2026-03-16T06:20:37Z" + model: gpt-5.4 + provider: openai + source_hash: fcce57da49256971420c4bb099aebb7944f8c7e8619b17b163da685add225001 source_path: channels/nostr.md workflow: 15 --- @@ -17,21 +17,21 @@ x-i18n: **状态:** 可选插件(默认禁用)。 -Nostr 是一个去中心化的社交网络协议。此渠道使 OpenClaw 能够通过 NIP-04 接收和回复加密私信(DMs)。 +Nostr 是一种用于社交网络的去中心化协议。此渠道使 OpenClaw 能够通过 NIP-04 接收并回复加密私信。 ## 安装(按需) ### 新手引导(推荐) -- 新手引导向导(`openclaw onboard`)和 `openclaw channels add` 会列出可选的渠道插件。 -- 选择 Nostr 会提示你按需安装插件。 +- 设置向导(`openclaw onboard`)和 `openclaw channels add` 会列出可选渠道插件。 +- 选择 Nostr 时,系统会提示你按需安装该插件。 -安装默认值: +安装默认行为: -- **Dev 渠道 + git checkout 可用:** 使用本地插件路径。 -- **Stable/Beta:** 从 npm 下载。 +- **Dev 渠道 + 可用的 git 检出:** 使用本地插件路径。 +- **稳定版 / Beta:** 从 npm 下载。 -你可以随时在提示中覆盖选择。 +你始终可以在提示中覆盖该选择。 ### 手动安装 @@ -39,24 +39,33 @@ Nostr 是一个去中心化的社交网络协议。此渠道使 OpenClaw 能够 openclaw plugins install @openclaw/nostr ``` -使用本地 checkout(开发工作流): +使用本地检出(dev 工作流): ```bash openclaw plugins install --link /extensions/nostr ``` -安装或启用插件后重启 Gateway 网关。 +安装或启用插件后,重启 Gateway 网关。 + +### 非交互式设置 + +```bash +openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY" +openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY" --relay-urls "wss://relay.damus.io,wss://relay.primal.net" +``` + +使用 `--use-env` 可将 `NOSTR_PRIVATE_KEY` 保留在环境中,而不是将密钥存储在配置里。 ## 快速设置 -1. 生成 Nostr 密钥对(如需要): +1. 生成一个 Nostr 密钥对(如有需要): ```bash -# 使用 nak +# Using nak nak key generate ``` -2. 添加到配置: +2. 添加到配置中: ```json { @@ -78,19 +87,19 @@ export NOSTR_PRIVATE_KEY="nsec1..." ## 配置参考 -| 键 | 类型 | 默认值 | 描述 | +| 键 | 类型 | 默认值 | 说明 | | ------------ | -------- | ------------------------------------------- | --------------------------- | | `privateKey` | string | 必填 | `nsec` 或十六进制格式的私钥 | | `relays` | string[] | `['wss://relay.damus.io', 'wss://nos.lol']` | 中继 URL(WebSocket) | | `dmPolicy` | string | `pairing` | 私信访问策略 | -| `allowFrom` | string[] | `[]` | 允许的发送者公钥 | -| `enabled` | boolean | `true` | 启用/禁用渠道 | +| `allowFrom` | string[] | `[]` | 允许的发送方公钥 | +| `enabled` | boolean | `true` | 启用 / 禁用渠道 | | `name` | string | - | 显示名称 | -| `profile` | object | - | NIP-01 个人资料元数据 | +| `profile` | object | - | NIP-01 资料元数据 | -## 个人资料元数据 +## 资料元数据 -个人资料数据作为 NIP-01 `kind:0` 事件发布。你可以从控制界面(Channels -> Nostr -> Profile)管理它,或直接在配置中设置。 +资料数据会作为 NIP-01 `kind:0` 事件发布。你可以在控制 UI 中管理它(Channels -> Nostr -> Profile),也可以直接在配置中设置。 示例: @@ -114,19 +123,19 @@ export NOSTR_PRIVATE_KEY="nsec1..." } ``` -注意事项: +注意: -- 个人资料 URL 必须使用 `https://`。 -- 从中继导入会合并字段并保留本地覆盖。 +- 资料 URL 必须使用 `https://`。 +- 从中继导入时会合并字段,并保留本地覆盖项。 ## 访问控制 ### 私信策略 -- **pairing**(默认):未知发送者会收到配对码。 +- **pairing**(默认):未知发送方会收到一个配对码。 - **allowlist**:只有 `allowFrom` 中的公钥可以发送私信。 -- **open**:公开接收私信(需要 `allowFrom: ["*"]`)。 -- **disabled**:忽略接收的私信。 +- **open**:公开接收入站私信(要求 `allowFrom: ["*"]`)。 +- **disabled**:忽略入站私信。 ### 允许列表示例 @@ -166,26 +175,26 @@ export NOSTR_PRIVATE_KEY="nsec1..." 提示: -- 使用 2-3 个中继以实现冗余。 +- 使用 2 到 3 个中继以实现冗余。 - 避免使用过多中继(延迟、重复)。 - 付费中继可以提高可靠性。 -- 本地中继适合测试(`ws://localhost:7777`)。 +- 本地中继也适合测试(`ws://localhost:7777`)。 ## 协议支持 -| NIP | 状态 | 描述 | -| ------ | ------ | ----------------------------- | -| NIP-01 | 已支持 | 基本事件格式 + 个人资料元数据 | -| NIP-04 | 已支持 | 加密私信(`kind:4`) | -| NIP-17 | 计划中 | 礼物包装私信 | -| NIP-44 | 计划中 | 版本化加密 | +| NIP | 状态 | 说明 | +| ------ | ------ | ------------------------- | +| NIP-01 | 已支持 | 基础事件格式 + 资料元数据 | +| NIP-04 | 已支持 | 加密私信(`kind:4`) | +| NIP-17 | 计划中 | Gift-wrapped 私信 | +| NIP-44 | 计划中 | 版本化加密 | ## 测试 ### 本地中继 ```bash -# 启动 strfry +# Start strfry docker run -p 7777:7777 ghcr.io/hoytech/strfry ``` @@ -203,38 +212,38 @@ docker run -p 7777:7777 ghcr.io/hoytech/strfry ### 手动测试 1. 从日志中记下机器人公钥(npub)。 -2. 打开 Nostr 客户端(Damus、Amethyst 等)。 -3. 向机器人公钥发送私信。 -4. 验证响应。 +2. 打开一个 Nostr 客户端(Damus、Amethyst 等)。 +3. 向该机器人公钥发送私信。 +4. 验证回复。 ## 故障排除 -### 未收到消息 +### 未接收到消息 -- 验证私钥是否有效。 -- 确保中继 URL 可访问并使用 `wss://`(本地使用 `ws://`)。 +- 验证私钥有效。 +- 确保中继 URL 可访问,并使用 `wss://`(本地则使用 `ws://`)。 - 确认 `enabled` 不是 `false`。 - 检查 Gateway 网关日志中的中继连接错误。 -### 未发送响应 +### 未发送回复 - 检查中继是否接受写入。 -- 验证出站连接。 +- 验证出站连接性。 - 注意中继速率限制。 -### 重复响应 +### 重复回复 -- 使用多个中继时属于正常现象。 -- 消息按事件 ID 去重;只有首次投递会触发响应。 +- 使用多个中继时这是预期行为。 +- 消息会按事件 ID 去重;只有首次投递会触发回复。 -## 安全 +## 安全性 - 切勿提交私钥。 -- 使用环境变量存储密钥。 -- 生产环境机器人考虑使用 `allowlist`。 +- 对密钥使用环境变量。 +- 对生产机器人考虑使用 `allowlist`。 ## 限制(MVP) - 仅支持私信(不支持群聊)。 - 不支持媒体附件。 -- 仅支持 NIP-04(计划支持 NIP-17 礼物包装)。 +- 仅支持 NIP-04(计划支持 NIP-17 gift-wrap)。 diff --git a/docs/zh-CN/channels/synology-chat.md b/docs/zh-CN/channels/synology-chat.md new file mode 100644 index 00000000000..4323e5d12d7 --- /dev/null +++ b/docs/zh-CN/channels/synology-chat.md @@ -0,0 +1,138 @@ +--- +read_when: + - 设置 OpenClaw 与 Synology Chat + - 调试 Synology Chat webhook 路由 +summary: Synology Chat webhook 设置与 OpenClaw 配置 +title: Synology Chat +x-i18n: + generated_at: "2026-03-16T06:20:51Z" + model: gpt-5.4 + provider: openai + source_hash: 7d77598ea759f89873a1edf0a3a7e7fedc1e4a7067709aaca6b999056a89eb1a + source_path: channels/synology-chat.md + workflow: 15 +--- + +# Synology Chat(插件) + +状态:通过插件支持,作为使用 Synology Chat webhook 的私信渠道。 +该插件接受来自 Synology Chat 出站 webhook 的入站消息,并通过 Synology Chat 入站 webhook 发送回复。 + +## 需要插件 + +Synology Chat 基于插件,不属于默认的核心渠道安装内容。 + +从本地检出安装: + +```bash +openclaw plugins install ./extensions/synology-chat +``` + +详情:[插件](/tools/plugin) + +## 快速设置 + +1. 安装并启用 Synology Chat 插件。 + - `openclaw onboard` 现在会在与 `openclaw channels add` 相同的渠道设置列表中显示 Synology Chat。 + - 非交互式设置:`openclaw channels add --channel synology-chat --token --url ` +2. 在 Synology Chat 集成中: + - 创建一个入站 webhook 并复制其 URL。 + - 使用你的 secret token 创建一个出站 webhook。 +3. 将出站 webhook URL 指向你的 OpenClaw Gateway 网关: + - 默认是 `https://gateway-host/webhook/synology`。 + - 或者使用你自定义的 `channels.synology-chat.webhookPath`。 +4. 在 OpenClaw 中完成设置。 + - 引导式:`openclaw onboard` + - 直接设置:`openclaw channels add --channel synology-chat --token --url ` +5. 重启 Gateway 网关,并向 Synology Chat 机器人发送一条私信。 + +最小配置: + +```json5 +{ + channels: { + "synology-chat": { + enabled: true, + token: "synology-outgoing-token", + incomingUrl: "https://nas.example.com/webapi/entry.cgi?api=SYNO.Chat.External&method=incoming&version=2&token=...", + webhookPath: "/webhook/synology", + dmPolicy: "allowlist", + allowedUserIds: ["123456"], + rateLimitPerMinute: 30, + allowInsecureSsl: false, + }, + }, +} +``` + +## 环境变量 + +对于默认账户,你可以使用环境变量: + +- `SYNOLOGY_CHAT_TOKEN` +- `SYNOLOGY_CHAT_INCOMING_URL` +- `SYNOLOGY_NAS_HOST` +- `SYNOLOGY_ALLOWED_USER_IDS`(逗号分隔) +- `SYNOLOGY_RATE_LIMIT` +- `OPENCLAW_BOT_NAME` + +配置值会覆盖环境变量。 + +## 私信策略与访问控制 + +- 推荐的默认值是 `dmPolicy: "allowlist"`。 +- `allowedUserIds` 接受 Synology 用户 ID 列表(或逗号分隔字符串)。 +- 在 `allowlist` 模式下,空的 `allowedUserIds` 列表会被视为配置错误,webhook 路由将不会启动(如需允许所有人,请使用 `dmPolicy: "open"`)。 +- `dmPolicy: "open"` 允许任何发送方。 +- `dmPolicy: "disabled"` 会阻止私信。 +- 配对批准可配合以下命令使用: + - `openclaw pairing list synology-chat` + - `openclaw pairing approve synology-chat ` + +## 出站投递 + +使用数字形式的 Synology Chat 用户 ID 作为目标。 + +示例: + +```bash +openclaw message send --channel synology-chat --target 123456 --text "Hello from OpenClaw" +openclaw message send --channel synology-chat --target synology-chat:123456 --text "Hello again" +``` + +支持通过基于 URL 的文件投递发送媒体。 + +## 多账户 + +支持在 `channels.synology-chat.accounts` 下配置多个 Synology Chat 账户。 +每个账户都可以覆盖 token、入站 URL、webhook 路径、私信策略和限制。 + +```json5 +{ + channels: { + "synology-chat": { + enabled: true, + accounts: { + default: { + token: "token-a", + incomingUrl: "https://nas-a.example.com/...token=...", + }, + alerts: { + token: "token-b", + incomingUrl: "https://nas-b.example.com/...token=...", + webhookPath: "/webhook/synology-alerts", + dmPolicy: "allowlist", + allowedUserIds: ["987654"], + }, + }, + }, + }, +} +``` + +## 安全说明 + +- 妥善保管 `token`,如果泄露请轮换。 +- 除非你明确可信任本地 NAS 的自签名证书,否则请保持 `allowInsecureSsl: false`。 +- 入站 webhook 请求会按 token 验证,并按发送方进行速率限制。 +- 生产环境优先使用 `dmPolicy: "allowlist"`。 diff --git a/docs/zh-CN/cli/index.md b/docs/zh-CN/cli/index.md index e7ae99ef935..46be3ef8ab1 100644 --- a/docs/zh-CN/cli/index.md +++ b/docs/zh-CN/cli/index.md @@ -1,14 +1,14 @@ --- read_when: - - 添加或修改 CLI 命令或选项 - - 为新命令界面编写文档 -summary: OpenClaw `openclaw` 命令、子命令和选项的 CLI 参考 + - 添加或修改 CLI 命令或选项时 + - 为新的命令界面编写文档时 +summary: "`openclaw` 命令、子命令和选项的 OpenClaw CLI 参考" title: CLI 参考 x-i18n: - generated_at: "2026-02-03T07:47:54Z" - model: claude-opus-4-5 - provider: pi - source_hash: a73923763d7b89d4b183f569d543927ffbfd1f3e02f9e66639913f6daf226850 + generated_at: "2026-03-16T06:22:35Z" + model: gpt-5.4 + provider: openai + source_hash: a2bca34fca64558a8d91fc640ad3880e79677e81d0f605083edc6cbe86bfba53 source_path: cli/index.md workflow: 15 --- @@ -23,8 +23,10 @@ x-i18n: - [`onboard`](/cli/onboard) - [`configure`](/cli/configure) - [`config`](/cli/config) +- [`completion`](/cli/completion) - [`doctor`](/cli/doctor) - [`dashboard`](/cli/dashboard) +- [`backup`](/cli/backup) - [`reset`](/cli/reset) - [`uninstall`](/cli/uninstall) - [`update`](/cli/update) @@ -40,6 +42,7 @@ x-i18n: - [`system`](/cli/system) - [`models`](/cli/models) - [`memory`](/cli/memory) +- [`directory`](/cli/directory) - [`nodes`](/cli/nodes) - [`devices`](/cli/devices) - [`node`](/cli/node) @@ -53,42 +56,46 @@ x-i18n: - [`hooks`](/cli/hooks) - [`webhooks`](/cli/webhooks) - [`pairing`](/cli/pairing) +- [`qr`](/cli/qr) - [`plugins`](/cli/plugins)(插件命令) - [`channels`](/cli/channels) - [`security`](/cli/security) +- [`secrets`](/cli/secrets) - [`skills`](/cli/skills) +- [`daemon`](/cli/daemon)(Gateway 网关服务命令的旧别名) +- [`clawbot`](/cli/clawbot)(旧别名命名空间) - [`voicecall`](/cli/voicecall)(插件;如已安装) ## 全局标志 -- `--dev`:将状态隔离到 `~/.openclaw-dev` 下并调整默认端口。 +- `--dev`:将状态隔离到 `~/.openclaw-dev` 下,并变更默认端口。 - `--profile `:将状态隔离到 `~/.openclaw-` 下。 - `--no-color`:禁用 ANSI 颜色。 -- `--update`:`openclaw update` 的简写(仅限源码安装)。 -- `-V`、`--version`、`-v`:打印版本并退出。 +- `--update`:`openclaw update` 的简写(仅适用于源码安装)。 +- `-V`, `--version`, `-v`:打印版本并退出。 ## 输出样式 - ANSI 颜色和进度指示器仅在 TTY 会话中渲染。 -- OSC-8 超链接在支持的终端中渲染为可点击链接;否则回退到纯 URL。 -- `--json`(以及支持的地方使用 `--plain`)禁用样式以获得干净输出。 -- `--no-color` 禁用 ANSI 样式;也支持 `NO_COLOR=1`。 -- 长时间运行的命令显示进度指示器(支持时使用 OSC 9;4)。 +- OSC-8 超链接会在受支持的终端中显示为可点击链接;否则会回退为纯 URL。 +- `--json`(以及在支持处的 `--plain`)会禁用样式,以获得干净输出。 +- `--no-color` 会禁用 ANSI 样式;同时也支持 `NO_COLOR=1`。 +- 长时间运行的命令会显示进度指示器(支持时使用 OSC 9;4)。 -## 颜色调色板 +## 调色板 -OpenClaw 在 CLI 输出中使用龙虾调色板。 +OpenClaw 在 CLI 输出中使用龙虾色调调色板。 -- `accent`(#FF5A2D):标题、标签、主要高亮。 -- `accentBright`(#FF7A3D):命令名称、强调。 -- `accentDim`(#D14A22):次要高亮文本。 -- `info`(#FF8A5B):信息性值。 -- `success`(#2FBF71):成功状态。 -- `warn`(#FFB020):警告、回退、注意。 -- `error`(#E23D2D):错误、失败。 -- `muted`(#8B7F77):弱化、元数据。 +- `accent` (#FF5A2D):标题、标签、主要高亮。 +- `accentBright` (#FF7A3D):命令名称、强调。 +- `accentDim` (#D14A22):次级高亮文本。 +- `info` (#FF8A5B):信息性值。 +- `success` (#2FBF71):成功状态。 +- `warn` (#FFB020):警告、回退、注意事项。 +- `error` (#E23D2D):错误、失败。 +- `muted` (#8B7F77):弱化显示、元数据。 -调色板权威来源:`src/terminal/palette.ts`(又名"lobster seam")。 +调色板唯一来源:`src/terminal/palette.ts`(也称为 “lobster seam”)。 ## 命令树 @@ -101,9 +108,17 @@ openclaw [--dev] [--profile ] get set unset + completion doctor + dashboard + backup + create + verify security audit + secrets + reload + migrate reset uninstall update @@ -115,6 +130,7 @@ openclaw [--dev] [--profile ] remove login logout + directory skills list info @@ -152,6 +168,13 @@ openclaw [--dev] [--profile ] stop restart run + daemon + status + install + uninstall + start + stop + restart logs system event @@ -238,49 +261,59 @@ openclaw [--dev] [--profile ] pairing list approve + qr + clawbot + qr docs dns setup tui ``` -注意:插件可以添加额外的顶级命令(例如 `openclaw voicecall`)。 +注意:插件可以添加额外的顶层命令(例如 `openclaw voicecall`)。 ## 安全 -- `openclaw security audit` — 审计配置 + 本地状态中常见的安全隐患。 +- `openclaw security audit` — 审计配置 + 本地状态中常见的安全陷阱。 - `openclaw security audit --deep` — 尽力进行实时 Gateway 网关探测。 -- `openclaw security audit --fix` — 收紧安全默认值并 chmod 状态/配置。 +- `openclaw security audit --fix` — 收紧安全默认值并对状态 / 配置执行 chmod。 + +## 密钥 + +- `openclaw secrets reload` — 重新解析引用,并以原子方式替换运行时快照。 +- `openclaw secrets audit` — 扫描明文残留、未解析引用和优先级漂移。 +- `openclaw secrets configure` — 用于提供商设置 + SecretRef 映射 + 预检 / 应用的交互式助手。 +- `openclaw secrets apply --from ` — 应用先前生成的计划(支持 `--dry-run`)。 ## 插件 管理扩展及其配置: -- `openclaw plugins list` — 发现插件(使用 `--json` 获取机器可读输出)。 +- `openclaw plugins list` — 发现插件(机器输出请使用 `--json`)。 - `openclaw plugins info ` — 显示插件详情。 - `openclaw plugins install ` — 安装插件(或将插件路径添加到 `plugins.load.paths`)。 - `openclaw plugins enable ` / `disable ` — 切换 `plugins.entries..enabled`。 - `openclaw plugins doctor` — 报告插件加载错误。 -大多数插件更改需要重启 Gateway 网关。参见 [/plugin](/tools/plugin)。 +大多数插件更改都需要重启 gateway。参见 [/plugin](/tools/plugin)。 -## 记忆 +## 内存 -对 `MEMORY.md` + `memory/*.md` 进行向量搜索: +对 `MEMORY.md` + `memory/*.md` 执行向量搜索: -- `openclaw memory status` — 显示索引统计。 -- `openclaw memory index` — 重新索引记忆文件。 -- `openclaw memory search ""` — 对记忆进行语义搜索。 +- `openclaw memory status` — 显示索引统计信息。 +- `openclaw memory index` — 重新索引内存文件。 +- `openclaw memory search ""`(或 `--query ""`)— 对内存执行语义搜索。 ## 聊天斜杠命令 聊天消息支持 `/...` 命令(文本和原生)。参见 [/tools/slash-commands](/tools/slash-commands)。 -亮点: +重点: - `/status` 用于快速诊断。 - `/config` 用于持久化配置更改。 -- `/debug` 用于仅运行时的配置覆盖(内存中,不写入磁盘;需要 `commands.debug: true`)。 +- `/debug` 用于仅运行时的配置覆盖(内存中,不写磁盘;要求 `commands.debug: true`)。 ## 设置 + 新手引导 @@ -291,32 +324,35 @@ openclaw [--dev] [--profile ] 选项: - `--workspace `:智能体工作区路径(默认 `~/.openclaw/workspace`)。 -- `--wizard`:运行新手引导向导。 +- `--wizard`:运行设置向导。 - `--non-interactive`:无提示运行向导。 - `--mode `:向导模式。 - `--remote-url `:远程 Gateway 网关 URL。 -- `--remote-token `:远程 Gateway 网关令牌。 +- `--remote-token `:远程 Gateway 网关 token。 -当存在任何向导标志(`--non-interactive`、`--mode`、`--remote-url`、`--remote-token`)时,向导自动运行。 +只要存在任意向导标志(`--non-interactive`, `--mode`, `--remote-url`, `--remote-token`),就会自动运行向导。 ### `onboard` -交互式向导,用于设置 Gateway 网关、工作区和 Skills。 +用于设置 gateway、工作区和 Skills 的交互式向导。 选项: - `--workspace ` -- `--reset`(在向导之前重置配置 + 凭证 + 会话 + 工作区) +- `--reset`(在运行向导前重置配置 + 凭据 + 会话) +- `--reset-scope `(默认 `config+creds+sessions`;使用 `full` 还会删除工作区) - `--non-interactive` - `--mode ` -- `--flow `(manual 是 advanced 的别名) -- `--auth-choice ` -- `--token-provider `(非交互式;与 `--auth-choice token` 配合使用) -- `--token `(非交互式;与 `--auth-choice token` 配合使用) +- `--flow `(`manual` 是 `advanced` 的别名) +- `--auth-choice ` +- `--token-provider `(非交互式;与 `--auth-choice token` 一起使用) +- `--token `(非交互式;与 `--auth-choice token` 一起使用) - `--token-profile-id `(非交互式;默认:`:manual`) - `--token-expires-in `(非交互式;例如 `365d`、`12h`) +- `--secret-input-mode `(默认 `plaintext`;使用 `ref` 可存储提供商默认环境引用,而非明文密钥) - `--anthropic-api-key ` - `--openai-api-key ` +- `--mistral-api-key ` - `--openrouter-api-key ` - `--ai-gateway-api-key ` - `--moonshot-api-key ` @@ -325,10 +361,17 @@ openclaw [--dev] [--profile ] - `--zai-api-key ` - `--minimax-api-key ` - `--opencode-zen-api-key ` +- `--opencode-go-api-key ` +- `--custom-base-url `(非交互式;与 `--auth-choice custom-api-key` 或 `--auth-choice ollama` 一起使用) +- `--custom-model-id `(非交互式;与 `--auth-choice custom-api-key` 或 `--auth-choice ollama` 一起使用) +- `--custom-api-key `(非交互式;可选;与 `--auth-choice custom-api-key` 一起使用;省略时回退到 `CUSTOM_API_KEY`) +- `--custom-provider-id `(非交互式;可选自定义提供商 id) +- `--custom-compatibility `(非交互式;可选;默认 `openai`) - `--gateway-port ` - `--gateway-bind ` - `--gateway-auth ` - `--gateway-token ` +- `--gateway-token-ref-env `(非交互式;将 `gateway.auth.token` 存储为环境 SecretRef;要求该环境变量已设置;不能与 `--gateway-token` 一起使用) - `--gateway-password ` - `--remote-url ` - `--remote-token ` @@ -341,35 +384,39 @@ openclaw [--dev] [--profile ] - `--skip-skills` - `--skip-health` - `--skip-ui` -- `--node-manager `(推荐 pnpm;不建议将 bun 用于 Gateway 网关运行时) +- `--node-manager `(推荐 pnpm;不推荐将 bun 用作 Gateway 网关运行时) - `--json` ### `configure` -交互式配置向导(模型、渠道、Skills、Gateway 网关)。 +交互式配置向导(模型、渠道、Skills、gateway)。 ### `config` -非交互式配置辅助工具(get/set/unset)。不带子命令运行 `openclaw config` 会启动向导。 +非交互式配置助手(get/set/unset/file/validate)。直接运行 `openclaw config` 而不带 +子命令会启动向导。 子命令: -- `config get `:打印配置值(点/括号路径)。 -- `config set `:设置值(JSON5 或原始字符串)。 -- `config unset `:删除值。 +- `config get `:打印一个配置值(点 / 方括号路径)。 +- `config set `:设置一个值(JSON5 或原始字符串)。 +- `config unset `:移除一个值。 +- `config file`:打印当前活动配置文件路径。 +- `config validate`:根据 schema 验证当前配置,而不启动 gateway。 +- `config validate --json`:输出机器可读的 JSON。 ### `doctor` -健康检查 + 快速修复(配置 + Gateway 网关 + 旧版服务)。 +健康检查 + 快速修复(配置 + gateway + 旧版服务)。 选项: -- `--no-workspace-suggestions`:禁用工作区记忆提示。 -- `--yes`:无提示接受默认值(无头模式)。 +- `--no-workspace-suggestions`:禁用工作区内存提示。 +- `--yes`:接受默认值而不提示(无头)。 - `--non-interactive`:跳过提示;仅应用安全迁移。 -- `--deep`:扫描系统服务以查找额外的 Gateway 网关安装。 +- `--deep`:扫描系统服务以查找额外的 gateway 安装。 -## 渠道辅助工具 +## 渠道助手 ### `channels` @@ -378,19 +425,21 @@ openclaw [--dev] [--profile ] 子命令: - `channels list`:显示已配置的渠道和认证配置文件。 -- `channels status`:检查 Gateway 网关可达性和渠道健康状况(`--probe` 运行额外检查;使用 `openclaw health` 或 `openclaw status --deep` 进行 Gateway 网关健康探测)。 -- 提示:`channels status` 在检测到常见配置错误时会打印带有建议修复的警告(然后指向 `openclaw doctor`)。 -- `channels logs`:显示 Gateway 网关日志文件中最近的渠道日志。 -- `channels add`:不传标志时使用向导式设置;标志切换到非交互模式。 -- `channels remove`:默认禁用;传 `--delete` 可无提示删除配置条目。 -- `channels login`:交互式渠道登录(仅限 WhatsApp Web)。 -- `channels logout`:登出渠道会话(如支持)。 +- `channels status`:检查 gateway 可达性和渠道健康状态(`--probe` 会运行额外检查;gateway 健康探测请使用 `openclaw health` 或 `openclaw status --deep`)。 +- 提示:如果能够检测到常见配置错误,`channels status` 会打印带建议修复方式的警告(随后指向 `openclaw doctor`)。 +- `channels logs`:显示 gateway 日志文件中的最近渠道日志。 +- `channels add`:未传入任何标志时为向导式设置;传入标志后切换为非交互模式。 + - 当向仍使用单账户顶层配置的渠道添加非默认账户时,OpenClaw 会先将账户作用域值移动到 `channels..accounts.default`,再写入新账户。 + - 非交互式 `channels add` 不会自动创建 / 升级绑定;仅渠道绑定会继续匹配默认账户。 +- `channels remove`:默认执行禁用;传入 `--delete` 可在无提示下删除配置项。 +- `channels login`:交互式渠道登录(仅 WhatsApp Web)。 +- `channels logout`:登出某个渠道会话(如支持)。 通用选项: - `--channel `:`whatsapp|telegram|discord|googlechat|slack|mattermost|signal|imessage|msteams` - `--account `:渠道账户 id(默认 `default`) -- `--name